""" constraint_graph_svg.py — Ahmad's ConstraintGraph compiler extension Compiles ConstraintGraph XML to both SVG visualization and executable runtime dict. """ import xml.etree.ElementTree as ET from collections import defaultdict, deque from typing import List, Dict, Any # ─── Node type emoji icons ──────────────────────────────────────────────────── NODE_ICONS = { "Input": "\U0001f9e0", # 🧠 "Memory": "\U0001f4da", # 📚 "Retrieval": "\U0001f50d", # 🔍 "Transform": "⚙", # ⚙ "Constraint": "⚖", # ⚖ "Proof": "\U0001f510", # 🔐 "Output": "\U0001f310", # 🌐 } # ─── SVG layout constants ───────────────────────────────────────────────────── CANVAS_W = 400 CANVAS_H = 600 NODE_W = 160 NODE_H = 40 NODE_RX = 8 CENTER_X = 200 def _node_top_y(index: int, total: int) -> int: """Return the y-coordinate of the top edge of node at position index.""" margin_top = 30 spacing = (CANVAS_H - margin_top - NODE_H) // max(total - 1, 1) return margin_top + index * spacing # ─── Topological sort (Kahn's algorithm) ───────────────────────────────────── def _topological_sort(nodes: List[dict], edges: List[dict]) -> List[str]: in_degree: Dict[str, int] = {n["id"]: 0 for n in nodes} adjacency: Dict[str, List[str]] = defaultdict(list) for e in edges: adjacency[e["from"]].append(e["to"]) in_degree[e["to"]] += 1 queue = deque(nid for nid, deg in in_degree.items() if deg == 0) order = [] while queue: nid = queue.popleft() order.append(nid) for neighbor in adjacency[nid]: in_degree[neighbor] -= 1 if in_degree[neighbor] == 0: queue.append(neighbor) if len(order) != len(nodes): raise ValueError("ConstraintGraph contains a cycle — topological sort failed.") return order # ─── XML parsing ───────────────────────────────────────────────────────────── def _parse_graph_xml(xml_source: str): root = ET.fromstring(xml_source) if root.tag != "graph": raise ValueError(f"Root element must be , got <{root.tag}>") nodes = [] edges = [] for child in root: if child.tag == "node": nodes.append({"id": child.attrib["id"], "type": child.attrib["type"]}) elif child.tag == "edge": edges.append({"from": child.attrib["from"], "to": child.attrib["to"]}) return nodes, edges # ─── SVG renderer ──────────────────────────────────────────────────────────── def _render_svg(nodes: List[dict], edges: List[dict], pipeline: List[str]) -> str: # Build index map: node id → position in pipeline pos_map = {nid: i for i, nid in enumerate(pipeline)} type_map = {n["id"]: n["type"] for n in nodes} total = len(pipeline) svg_lines = [ f'', " ", " ", " ", " ", " ", " ", ] # Compute node rects node_rects: Dict[str, dict] = {} for nid in pipeline: idx = pos_map[nid] x = CENTER_X - NODE_W // 2 y = _node_top_y(idx, total) node_rects[nid] = {"x": x, "y": y} # Draw edges first (behind nodes) for edge in edges: src = edge["from"] dst = edge["to"] if src not in node_rects or dst not in node_rects: continue sx = CENTER_X sy = node_rects[src]["y"] + NODE_H dx = CENTER_X dy = node_rects[dst]["y"] svg_lines.append( f' ' ) # Draw nodes for nid in pipeline: node_type = type_map[nid] icon = NODE_ICONS.get(node_type, "?") x = node_rects[nid]["x"] y = node_rects[nid]["y"] label = f"{icon} {node_type}" cx = x + NODE_W // 2 cy = y + NODE_H // 2 + 5 # vertical center of text svg_lines.append( f' ' ) svg_lines.append( f' ' f'{label}' ) svg_lines.append("") return "\n".join(svg_lines) # ─── ConstraintGraphCompiler ────────────────────────────────────────────────── class ConstraintGraphCompiler: """ Ahmad's ConstraintGraph compiler. Takes ConstraintGraph XML and compiles to: - SVG visualization (compile_to_svg) - Executable runtime dict (compile_to_runtime) """ def compile_to_svg(self, xml_source: str) -> str: """ Compile ConstraintGraph XML to an SVG string. Nodes are laid out vertically in topological order with arrowhead edges. """ nodes, edges = _parse_graph_xml(xml_source) pipeline = _topological_sort(nodes, edges) return _render_svg(nodes, edges, pipeline) def compile_to_runtime(self, xml_source: str) -> Dict[str, Any]: """ Compile ConstraintGraph XML to an executable runtime representation. Returns: { "nodes": [{"id": ..., "type": ...}, ...], "edges": [{"from": ..., "to": ...}, ...], "pipeline": [] } """ nodes, edges = _parse_graph_xml(xml_source) pipeline = _topological_sort(nodes, edges) return { "nodes": nodes, "edges": edges, "pipeline": pipeline, } # ─── CLI ────────────────────────────────────────────────────────────────────── def _cli(): import sys if len(sys.argv) < 2 or sys.argv[1] in ("-h", "--help"): print("Usage: constraint-graph [output.svg|--runtime]") print(" Compiles a XML to SVG or prints the runtime dict.") sys.exit(0) src = open(sys.argv[1], encoding="utf-8").read() compiler = ConstraintGraphCompiler() if len(sys.argv) >= 3 and sys.argv[2] == "--runtime": import json print(json.dumps(compiler.compile_to_runtime(src), indent=2)) else: svg = compiler.compile_to_svg(src) if len(sys.argv) >= 3: open(sys.argv[2], "w", encoding="utf-8").write(svg) else: print(svg) if __name__ == "__main__": _cli()