Extract types, procs, includes, and calls from a .dm/.dme file.
(path: Path)
| 15218 | # the generic class-body walker doesn't fit well. |
| 15219 | |
| 15220 | def extract_dm(path: Path) -> dict: |
| 15221 | """Extract types, procs, includes, and calls from a .dm/.dme file.""" |
| 15222 | try: |
| 15223 | import tree_sitter_dm as tsdm |
| 15224 | from tree_sitter import Language, Parser |
| 15225 | except ImportError: |
| 15226 | return {"nodes": [], "edges": [], "error": "tree-sitter-dm not installed"} |
| 15227 | try: |
| 15228 | language = Language(tsdm.language()) |
| 15229 | parser = Parser(language) |
| 15230 | source = path.read_bytes() |
| 15231 | tree = parser.parse(source) |
| 15232 | root = tree.root_node |
| 15233 | except Exception as e: |
| 15234 | return {"nodes": [], "edges": [], "error": str(e)} |
| 15235 | |
| 15236 | stem = _file_stem(path) |
| 15237 | str_path = str(path) |
| 15238 | nodes: list[dict] = [] |
| 15239 | edges: list[dict] = [] |
| 15240 | seen_ids: set[str] = set() |
| 15241 | function_bodies: list[tuple[str, Any, "str | None"]] = [] |
| 15242 | |
| 15243 | def add_node(nid: str, label: str, line: int) -> None: |
| 15244 | if nid and nid not in seen_ids: |
| 15245 | seen_ids.add(nid) |
| 15246 | nodes.append({"id": nid, "label": label, "file_type": "code", |
| 15247 | "source_file": str_path, "source_location": f"L{line}"}) |
| 15248 | |
| 15249 | def add_edge(src: str, tgt: str, relation: str, line: int, |
| 15250 | confidence: str = "EXTRACTED", weight: float = 1.0, |
| 15251 | context: str | None = None) -> None: |
| 15252 | if not src or not tgt or src == tgt: |
| 15253 | return |
| 15254 | edge: dict = {"source": src, "target": tgt, "relation": relation, |
| 15255 | "confidence": confidence, "source_file": str_path, |
| 15256 | "source_location": f"L{line}", "weight": weight} |
| 15257 | if context: |
| 15258 | edge["context"] = context |
| 15259 | edges.append(edge) |
| 15260 | |
| 15261 | file_nid = _make_id(str(path)) |
| 15262 | add_node(file_nid, path.name, 1) |
| 15263 | |
| 15264 | def _type_path_text(node) -> str: |
| 15265 | return _read_text(node, source).strip() |
| 15266 | |
| 15267 | def _ensure_type(path_text: str, line: int) -> str: |
| 15268 | nid = _make_id(stem, path_text) |
| 15269 | add_node(nid, path_text, line) |
| 15270 | return nid |
| 15271 | |
| 15272 | def _find_child(node, type_name: str): |
| 15273 | for c in node.children: |
| 15274 | if c.type == type_name: |
| 15275 | return c |
| 15276 | return None |
| 15277 |