summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAsko Nõmm <asko@nmm.ee>2024-12-27 22:55:13 +0200
committerAsko Nõmm <asko@nmm.ee>2024-12-27 22:55:13 +0200
commitc539b8f72b9d47453a09febe91ec551e11252f80 (patch)
tree07ae5dfa42e3d28e8a45acbd7fdfa7b887abd671
bump
-rw-r--r--.gitignore13
-rw-r--r--.python-version1
-rw-r--r--README.md5
-rw-r--r--hello.py9
-rw-r--r--htmtl/__init__.py1
-rw-r--r--htmtl/htmtl.py151
-rw-r--r--pyproject.toml19
7 files changed, 199 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..185c342
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,13 @@
+# Python-generated files
+__pycache__/
+*.py[oc]
+build/
+dist/
+wheels/
+*.egg-info
+
+# Virtual environments
+.venv
+
+# Other
+.idea/
diff --git a/.python-version b/.python-version
new file mode 100644
index 0000000..24ee5b1
--- /dev/null
+++ b/.python-version
@@ -0,0 +1 @@
+3.13
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0d94bda
--- /dev/null
+++ b/README.md
@@ -0,0 +1,5 @@
+# HTMTL
+
+HTMTL (HyperText Markup _Templating_ Language) is a templating language that uses HTML attributes for its rendering logic.
+It is both a subset and superset of HTML, meaning that valid HTML is also valid HTMTL and valid HTMTL is also valid HTML, allowing
+you to use any editor without needing any additional plugins. \ No newline at end of file
diff --git a/hello.py b/hello.py
new file mode 100644
index 0000000..504affe
--- /dev/null
+++ b/hello.py
@@ -0,0 +1,9 @@
+from htmtl import Htmtl
+
+
+def main():
+ Htmtl("<div>Hello World<span>asdasd<a>asd</a></span></div>").toHtml()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/htmtl/__init__.py b/htmtl/__init__.py
new file mode 100644
index 0000000..7a91412
--- /dev/null
+++ b/htmtl/__init__.py
@@ -0,0 +1 @@
+from .htmtl import Htmtl \ No newline at end of file
diff --git a/htmtl/htmtl.py b/htmtl/htmtl.py
new file mode 100644
index 0000000..4a50043
--- /dev/null
+++ b/htmtl/htmtl.py
@@ -0,0 +1,151 @@
+from __future__ import annotations
+from typing import Dict, Any, Tuple, Callable, Optional
+
+
+class Node:
+ name: str
+ attributes: Dict[str, str]
+ children: list[Node]
+
+ def __init__(self, name: str, attributes: Dict[str, str], children: list[Node]):
+ self.name = name
+ self.attributes = attributes
+ self.children = []
+
+
+class IRBlockPosNode:
+ name: str
+ coords: Tuple[int, int]
+ moved: bool
+ children: list[IRBlockPosNode]
+
+ def __init__(self, name: str, coords: Tuple[int, int]):
+ self.name = name
+ self.coords = coords
+
+
+class IRBlockNode:
+ name: str
+ children: list[IRBlockNode]
+
+ def __init__(self, name: str, children: list[IRBlockNode]):
+ self.name = name
+ self.moved = False
+ self.children = children
+
+
+class Htmtl:
+ __template: str = ""
+ __ir_block_pos_nodes: list[IRBlockPosNode] = []
+ __ir_block_nodes: list[IRBlockNode] = []
+ __block_elements = [
+ "div", "span", "a"
+ ]
+ __inline_elements = [
+ "img"
+ ]
+
+ def __init__(self, template: str):
+ self.__template = template
+ self.__ir_block_pos_nodes = []
+ self.__ir_block_nodes = []
+ self.__parse_ir_block_pos_nodes()
+ self.__join_ir_block_pos_nodes()
+ self.__parse_ir_block_nodes()
+
+ def __parse_ir_block_pos_nodes(self):
+ start = None
+ end = None
+
+ for idx, part in enumerate(self.__template):
+ if part == "<":
+ start = idx
+
+ if part == ">":
+ end = idx + 1
+
+ if start is not None and end is not None:
+ tag = self.__template[start:end]
+
+ if tag.startswith("</"):
+ self.__maybe_close_ir_block_pos_node(tag, end)
+ start = None
+ end = None
+ continue
+
+ name = tag[1:-1].split(" ")[0].strip()
+
+ if name in self.__block_elements:
+ self.__ir_block_pos_nodes.append(
+ IRBlockPosNode(name=name, coords=(end, 0))
+ )
+
+ start = None
+ end = None
+
+ def __maybe_close_ir_block_pos_node(self, tag: str, coord: int):
+ el_name = tag[2:-1].split(' ')[0].strip()
+ match = self.__find_last_match(self.__ir_block_pos_nodes, lambda node: node.name == el_name)
+
+ if match is not None:
+ [idx, last_ir_pos_node] = match
+ last_ir_pos_node.coords = (last_ir_pos_node.coords[0], coord)
+ self.__ir_block_pos_nodes[idx] = last_ir_pos_node
+
+ def __join_ir_block_pos_nodes(self):
+ processed_coords = set()
+
+ for node in self.__ir_block_pos_nodes:
+ if node.coords in processed_coords:
+ continue
+
+ ir_block_pos_nodes_within = self.__find_block_pos_nodes_in_coords(node.coords)
+ node.children = self.__recursively_build_ir_block_pos_node_children(ir_block_pos_nodes_within, processed_coords)
+
+ self.__ir_block_pos_nodes = [
+ node for node in self.__ir_block_pos_nodes if node.coords not in processed_coords
+ ]
+
+ def __recursively_build_ir_block_pos_node_children(self, child_nodes: list[Tuple[int, IRBlockPosNode]], processed_coords: set):
+ children = []
+
+ for idx, child_node in child_nodes:
+ if child_node.coords in processed_coords:
+ continue
+
+ processed_coords.add(child_node.coords)
+ child_node_children = self.__find_block_pos_nodes_in_coords(child_node.coords)
+ child_node.children = self.__recursively_build_ir_block_pos_node_children(child_node_children, processed_coords)
+ children.append(child_node)
+
+ return children
+
+ def __find_block_pos_nodes_in_coords(self, coords: Tuple[int, int]) -> list[Tuple[int, IRBlockPosNode]]:
+ found_block_position_nodes = []
+ [start, end] = coords
+
+ for idx, ir_block_position_node in enumerate(self.__ir_block_pos_nodes):
+ [iter_start, iter_end] = ir_block_position_node.coords
+
+ if iter_start > start and iter_end < end:
+ found_block_position_nodes.append((idx, ir_block_position_node))
+
+ return found_block_position_nodes
+
+ def __parse_ir_block_nodes(self):
+ pass
+
+ @staticmethod
+ def __find_last_match(arr: list[Any], condition: Callable[[Any], bool]) -> Optional[Tuple[int, Any]]:
+ idx = len(arr)
+
+ for item in reversed(arr):
+ idx -= 1
+
+ if condition(item):
+ return idx, item
+
+ return None
+
+ def toHtml(self):
+ pass
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..b058883
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,19 @@
+[project]
+name = "htmtl"
+version = "0.1.0"
+description = "A templating language that is both a superset and subset of HTML."
+readme = "README.md"
+requires-python = ">=3.10"
+authors = [
+ { name = "Asko Nõmm", email = "asko@nmm.ee" }
+]
+dependencies = []
+classifiers = [
+ "Programming Language :: Python :: 3",
+ "License :: OSI Approved :: MIT License",
+ "Operating System :: OS Independent",
+ "Development Status :: 1 - Planning"
+]
+
+[tool.black]
+line-length = 100