Coverage for phml\core\parser\__init__.py: 75%
53 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
1"""phml.core.parser
3The core parsing module for phml. Handles parsing html and phmls strings
4along with json.
6Exposes phml.core.parser.Parser which handles all parsing functionality.
7"""
9from json import loads
10from pathlib import Path
11from typing import Callable, Optional
13from phml.nodes import AST, Root
15from .hypertextMarkupParser import HypertextMarkupParser
16from .json import json_to_ast
18__all__ = ["Parser"]
21class Parser:
22 """Primary logic to handle everything with a phml file.
24 This class can parse files as phml files and create an ast.
25 The ast and the nodes themselfs can translate themselves to;
26 html, phml, and json. The ast can recursively return itself as
27 an html string. However, only this class can process the python
28 blocks inside of the phml file.
30 Call Parser.convert() and pass any kwargs you wish to be exposed to
31 the process that processes the python. You may also use Parser.util to
32 pass extensions to convert and manipulate the html along with the python
33 processing.
34 """
36 parser: HypertextMarkupParser
37 """The custom builtin `html.parser` class that builds phml ast."""
39 ast: AST
40 """The recursive node tree of the phml ast."""
42 def __init__(self):
43 self.phml_parser = HypertextMarkupParser()
44 self.ast = None
46 def load(self, path: str | Path, handler: Optional[Callable] = None):
47 """Parse a given phml file to AST following hast and unist.
49 When finished the PHML.ast variable will be populated with the
50 resulting ast.
52 Args:
53 path (str | Path): The path to the file that should be parsed.
54 handler (Callable | None): A function to call instead of the built in
55 parser to parse to a phml.AST. Must take a string and return a phml.AST.
56 """
58 with open(path, "r", encoding="utf-8") as source:
59 src = source.read()
61 if handler is None:
62 path = Path(path)
64 if path.suffix == ".json":
65 self.ast = AST(json_to_ast(loads(src)))
66 else:
67 self.phml_parser.reset()
68 self.phml_parser.cur = Root()
70 try:
71 self.phml_parser.feed(src)
72 if len(self.phml_parser.cur_tags) > 0:
73 last = self.phml_parser.cur_tags[-1].position
74 raise Exception(
75 f"Unbalanced tags in source file '{path}' at [{last.start.line}:{last.start.column}]"
76 )
77 self.ast = AST(self.phml_parser.cur)
78 except Exception as e:
79 self.ast = None
80 raise Exception(f"'{path}': {e}")
81 else:
82 self.ast = handler(src)
84 return self
86 def parse(self, data: str | dict, handler: Optional[Callable] = None):
87 """Parse data from a phml/html string or from a dict to a phml ast.
89 Args:
90 data (str | dict): Data to parse in to a ast
91 data_type (str): Can be `HTML`, `PHML`, `MARKDOWN`, or `JSON` which
92 tells parser how to parse the data. Otherwise it will assume
93 str data to be html/phml and dict as `json`.
94 handler (Callable | None): A function to call instead of the built in
95 parser to parse to a phml.AST. Must take a string and return a phml.AST.
96 """
97 if handler is None:
98 if isinstance(data, dict):
99 self.ast = AST(json_to_ast(data))
100 elif isinstance(data, str):
101 self.phml_parser.reset()
102 self.phml_parser.cur = Root()
104 try:
105 self.phml_parser.feed(data)
106 if len(self.phml_parser.cur_tags) > 0:
107 last = self.phml_parser.cur_tags[-1].position
108 raise Exception(
109 f"Unbalanced tags in source at [{last.start.line}:{last.start.column}]"
110 )
111 self.ast = AST(self.phml_parser.cur)
112 except Exception as e:
113 self.ast = None
114 raise Exception(f"{data[:6] + '...' if len(data) > 6 else data}: {e}")
115 else:
116 self.ast = handler(data)
118 return self