phml.core
All core parsing, compiling, and valid file_types.
18class Compiler: 19 """Used to compile phml into other formats. HTML, PHML, 20 JSON, Markdown, etc... 21 """ 22 23 ast: AST 24 """phml ast used by the compiler to generate a new format.""" 25 26 def __init__( 27 self, 28 ast: Optional[AST] = None, 29 components: Optional[dict[str, dict[str, list | All_Nodes]]] = None, 30 ): 31 self.ast = ast 32 self.components = components or {} 33 34 def add( 35 self, 36 *components: dict[str, dict[str, list | All_Nodes] | AST] 37 | tuple[str, dict[str, list | All_Nodes] | AST], 38 ): 39 """Add a component to the compilers component list. 40 41 Components passed in can be of a few types. It can also be a dictionary of str 42 being the name of the element to be replaced. The name can be snake case, camel 43 case, or pascal cased. The value can either be the parsed result of the component 44 from phml.utils.parse_component() or the parsed ast of the component. Lastely, 45 the component can be a tuple. The first value is the name of the element to be 46 replaced; with the second value being either the parsed result of the component 47 or the component's ast. 48 49 Note: 50 Any duplicate components will be replaced. 51 52 Args: 53 components: Any number values indicating 54 name of the component and the the component. The name is used 55 to replace a element with the tag==name. 56 """ 57 58 for component in components: 59 if isinstance(component, dict): 60 for key, value in component.items(): 61 if isinstance(value, AST): 62 self.components[tag_from_file(key)] = parse_component(value) 63 else: 64 self.components[tag_from_file(key)] = value 65 elif isinstance(component, tuple): 66 if isinstance(component[1], AST): 67 self.components[tag_from_file(component[0])] = parse_component(component[1]) 68 else: 69 self.components[tag_from_file(component[0])] = component[1] 70 71 return self 72 73 def remove(self, *components: str | All_Nodes): 74 """Takes either component names or components and removes them 75 from the dictionary. 76 77 Args: 78 components (str | All_Nodes): Any str name of components or 79 node value to remove from the components list in the compiler. 80 """ 81 for component in components: 82 if isinstance(component, str): 83 if component in self.components: 84 self.components.pop(component, None) 85 else: 86 raise KeyError(f"Invalid component name {component}") 87 elif isinstance(component, All_Nodes): 88 for key, value in self.components: 89 if isinstance(value, dict) and value["component"] == component: 90 self.components.pop(key, None) 91 break 92 93 if value == components: 94 self.components.pop(key, None) 95 break 96 97 return self 98 99 def compile( 100 self, 101 ast: Optional[AST] = None, 102 to_format: str = HTML, 103 indent: Optional[int] = None, 104 handler: Optional[Callable] = None, 105 **kwargs: Any, 106 ) -> str: 107 """Execute compilation to a different format.""" 108 109 ast = ast or self.ast 110 111 if ast is None: 112 raise Exception("Must provide an ast to compile.") 113 114 doctypes = [dt for dt in visit_children(ast.tree) if test(dt, "doctype")] 115 if len(doctypes) == 0: 116 ast.tree.children.insert(0, DocType(parent=ast.tree)) 117 118 if to_format == PHML: 119 return phml(ast, indent or 4) 120 121 if to_format == HTML: 122 return html(ast, self.components, indent or 4, **kwargs) 123 124 if to_format == JSON: 125 return json(ast, indent or 2) 126 127 if handler is None: 128 raise Exception(f"Unkown format < { to_format } >") 129 130 return handler(ast, indent)
Used to compile phml into other formats. HTML, PHML, JSON, Markdown, etc...
34 def add( 35 self, 36 *components: dict[str, dict[str, list | All_Nodes] | AST] 37 | tuple[str, dict[str, list | All_Nodes] | AST], 38 ): 39 """Add a component to the compilers component list. 40 41 Components passed in can be of a few types. It can also be a dictionary of str 42 being the name of the element to be replaced. The name can be snake case, camel 43 case, or pascal cased. The value can either be the parsed result of the component 44 from phml.utils.parse_component() or the parsed ast of the component. Lastely, 45 the component can be a tuple. The first value is the name of the element to be 46 replaced; with the second value being either the parsed result of the component 47 or the component's ast. 48 49 Note: 50 Any duplicate components will be replaced. 51 52 Args: 53 components: Any number values indicating 54 name of the component and the the component. The name is used 55 to replace a element with the tag==name. 56 """ 57 58 for component in components: 59 if isinstance(component, dict): 60 for key, value in component.items(): 61 if isinstance(value, AST): 62 self.components[tag_from_file(key)] = parse_component(value) 63 else: 64 self.components[tag_from_file(key)] = value 65 elif isinstance(component, tuple): 66 if isinstance(component[1], AST): 67 self.components[tag_from_file(component[0])] = parse_component(component[1]) 68 else: 69 self.components[tag_from_file(component[0])] = component[1] 70 71 return self
Add a component to the compilers component list.
Components passed in can be of a few types. It can also be a dictionary of str being the name of the element to be replaced. The name can be snake case, camel case, or pascal cased. The value can either be the parsed result of the component from phml.utils.parse_component() or the parsed ast of the component. Lastely, the component can be a tuple. The first value is the name of the element to be replaced; with the second value being either the parsed result of the component or the component's ast.
Note
Any duplicate components will be replaced.
Args
- components: Any number values indicating
- name of the component and the the component. The name is used
- to replace a element with the tag==name.
73 def remove(self, *components: str | All_Nodes): 74 """Takes either component names or components and removes them 75 from the dictionary. 76 77 Args: 78 components (str | All_Nodes): Any str name of components or 79 node value to remove from the components list in the compiler. 80 """ 81 for component in components: 82 if isinstance(component, str): 83 if component in self.components: 84 self.components.pop(component, None) 85 else: 86 raise KeyError(f"Invalid component name {component}") 87 elif isinstance(component, All_Nodes): 88 for key, value in self.components: 89 if isinstance(value, dict) and value["component"] == component: 90 self.components.pop(key, None) 91 break 92 93 if value == components: 94 self.components.pop(key, None) 95 break 96 97 return self
Takes either component names or components and removes them from the dictionary.
Args
- components (str | All_Nodes): Any str name of components or
- node value to remove from the components list in the compiler.
99 def compile( 100 self, 101 ast: Optional[AST] = None, 102 to_format: str = HTML, 103 indent: Optional[int] = None, 104 handler: Optional[Callable] = None, 105 **kwargs: Any, 106 ) -> str: 107 """Execute compilation to a different format.""" 108 109 ast = ast or self.ast 110 111 if ast is None: 112 raise Exception("Must provide an ast to compile.") 113 114 doctypes = [dt for dt in visit_children(ast.tree) if test(dt, "doctype")] 115 if len(doctypes) == 0: 116 ast.tree.children.insert(0, DocType(parent=ast.tree)) 117 118 if to_format == PHML: 119 return phml(ast, indent or 4) 120 121 if to_format == HTML: 122 return html(ast, self.components, indent or 4, **kwargs) 123 124 if to_format == JSON: 125 return json(ast, indent or 2) 126 127 if handler is None: 128 raise Exception(f"Unkown format < { to_format } >") 129 130 return handler(ast, indent)
Execute compilation to a different format.
22class Parser: 23 """Primary logic to handle everything with a phml file. 24 25 This class can parse files as phml files and create an ast. 26 The ast and the nodes themselfs can translate themselves to; 27 html, phml, and json. The ast can recursively return itself as 28 an html string. However, only this class can process the python 29 blocks inside of the phml file. 30 31 Call Parser.convert() and pass any kwargs you wish to be exposed to 32 the process that processes the python. You may also use Parser.util to 33 pass extensions to convert and manipulate the html along with the python 34 processing. 35 """ 36 37 parser: HypertextMarkupParser 38 """The custom builtin `html.parser` class that builds phml ast.""" 39 40 ast: AST 41 """The recursive node tree of the phml ast.""" 42 43 def __init__(self): 44 self.phml_parser = HypertextMarkupParser() 45 self.ast = None 46 47 def load(self, path: str | Path, handler: Optional[Callable] = None): 48 """Parse a given phml file to AST following hast and unist. 49 50 When finished the PHML.ast variable will be populated with the 51 resulting ast. 52 53 Args: 54 path (str | Path): The path to the file that should be parsed. 55 handler (Callable | None): A function to call instead of the built in 56 parser to parse to a phml.AST. Must take a string and return a phml.AST. 57 """ 58 59 with open(path, "r", encoding="utf-8") as source: 60 src = source.read() 61 62 if handler is None: 63 path = Path(path) 64 65 if path.suffix == ".json": 66 self.ast = AST(json_to_ast(loads(src))) 67 else: 68 self.phml_parser.reset() 69 self.phml_parser.cur = Root() 70 71 try: 72 self.phml_parser.feed(src) 73 if len(self.phml_parser.cur_tags) > 0: 74 last = self.phml_parser.cur_tags[-1].position 75 raise Exception( 76 f"Unbalanced tags in source file '{path}' at \ 77[{last.start.line}:{last.start.column}]" 78 ) 79 self.ast = AST(self.phml_parser.cur) 80 except Exception as exception: 81 self.ast = None 82 raise Exception(f"'{path}': {exception}") from exception 83 else: 84 self.ast = handler(src) 85 86 return self 87 88 def parse(self, data: str | dict, handler: Optional[Callable] = None): 89 """Parse data from a phml/html string or from a dict to a phml ast. 90 91 Args: 92 data (str | dict): Data to parse in to a ast 93 data_type (str): Can be `HTML`, `PHML`, `MARKDOWN`, or `JSON` which 94 tells parser how to parse the data. Otherwise it will assume 95 str data to be html/phml and dict as `json`. 96 handler (Callable | None): A function to call instead of the built in 97 parser to parse to a phml.AST. Must take a string and return a phml.AST. 98 """ 99 if handler is None: 100 if isinstance(data, dict): 101 self.ast = AST(json_to_ast(data)) 102 elif isinstance(data, str): 103 self.phml_parser.reset() 104 self.phml_parser.cur = Root() 105 106 try: 107 self.phml_parser.feed(data) 108 if len(self.phml_parser.cur_tags) > 0: 109 last = self.phml_parser.cur_tags[-1].position 110 raise Exception( 111 f"Unbalanced tags in source at [{last.start.line}:{last.start.column}]" 112 ) 113 self.ast = AST(self.phml_parser.cur) 114 except Exception as exception: 115 self.ast = None 116 raise Exception( 117 f"{data[:6] + '...' if len(data) > 6 else data}\ 118: {exception}" 119 ) from exception 120 else: 121 self.ast = handler(data) 122 123 return self
Primary logic to handle everything with a phml file.
This class can parse files as phml files and create an ast. The ast and the nodes themselfs can translate themselves to; html, phml, and json. The ast can recursively return itself as an html string. However, only this class can process the python blocks inside of the phml file.
Call Parser.convert() and pass any kwargs you wish to be exposed to the process that processes the python. You may also use Parser.util to pass extensions to convert and manipulate the html along with the python processing.
The custom builtin html.parser
class that builds phml ast.
47 def load(self, path: str | Path, handler: Optional[Callable] = None): 48 """Parse a given phml file to AST following hast and unist. 49 50 When finished the PHML.ast variable will be populated with the 51 resulting ast. 52 53 Args: 54 path (str | Path): The path to the file that should be parsed. 55 handler (Callable | None): A function to call instead of the built in 56 parser to parse to a phml.AST. Must take a string and return a phml.AST. 57 """ 58 59 with open(path, "r", encoding="utf-8") as source: 60 src = source.read() 61 62 if handler is None: 63 path = Path(path) 64 65 if path.suffix == ".json": 66 self.ast = AST(json_to_ast(loads(src))) 67 else: 68 self.phml_parser.reset() 69 self.phml_parser.cur = Root() 70 71 try: 72 self.phml_parser.feed(src) 73 if len(self.phml_parser.cur_tags) > 0: 74 last = self.phml_parser.cur_tags[-1].position 75 raise Exception( 76 f"Unbalanced tags in source file '{path}' at \ 77[{last.start.line}:{last.start.column}]" 78 ) 79 self.ast = AST(self.phml_parser.cur) 80 except Exception as exception: 81 self.ast = None 82 raise Exception(f"'{path}': {exception}") from exception 83 else: 84 self.ast = handler(src) 85 86 return self
Parse a given phml file to AST following hast and unist.
When finished the PHML.ast variable will be populated with the resulting ast.
Args
88 def parse(self, data: str | dict, handler: Optional[Callable] = None): 89 """Parse data from a phml/html string or from a dict to a phml ast. 90 91 Args: 92 data (str | dict): Data to parse in to a ast 93 data_type (str): Can be `HTML`, `PHML`, `MARKDOWN`, or `JSON` which 94 tells parser how to parse the data. Otherwise it will assume 95 str data to be html/phml and dict as `json`. 96 handler (Callable | None): A function to call instead of the built in 97 parser to parse to a phml.AST. Must take a string and return a phml.AST. 98 """ 99 if handler is None: 100 if isinstance(data, dict): 101 self.ast = AST(json_to_ast(data)) 102 elif isinstance(data, str): 103 self.phml_parser.reset() 104 self.phml_parser.cur = Root() 105 106 try: 107 self.phml_parser.feed(data) 108 if len(self.phml_parser.cur_tags) > 0: 109 last = self.phml_parser.cur_tags[-1].position 110 raise Exception( 111 f"Unbalanced tags in source at [{last.start.line}:{last.start.column}]" 112 ) 113 self.ast = AST(self.phml_parser.cur) 114 except Exception as exception: 115 self.ast = None 116 raise Exception( 117 f"{data[:6] + '...' if len(data) > 6 else data}\ 118: {exception}" 119 ) from exception 120 else: 121 self.ast = handler(data) 122 123 return self
Parse data from a phml/html string or from a dict to a phml ast.
Args
- data (str | dict): Data to parse in to a ast
- data_type (str): Can be
HTML
,PHML
,MARKDOWN
, orJSON
which - tells parser how to parse the data. Otherwise it will assume
- str data to be html/phml and dict as
json
. - handler (Callable | None): A function to call instead of the built in
- parser to parse to a phml.AST. Must take a string and return a phml.AST.