graphitect 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- graphify/__init__.py +30 -0
- graphify/__main__.py +757 -0
- graphify/_minhash.py +107 -0
- graphify/affected.py +318 -0
- graphify/always_on/agents-md.md +12 -0
- graphify/always_on/antigravity-rules.md +14 -0
- graphify/always_on/claude-md.md +9 -0
- graphify/always_on/gemini-md.md +9 -0
- graphify/always_on/kiro-steering.md +5 -0
- graphify/always_on/vscode-instructions.md +17 -0
- graphify/analyze.py +769 -0
- graphify/benchmark.py +152 -0
- graphify/build.py +2300 -0
- graphify/cache.py +1746 -0
- graphify/callflow_html.py +2051 -0
- graphify/cargo_introspect.py +109 -0
- graphify/cli.py +4745 -0
- graphify/cluster.py +409 -0
- graphify/command-kilo.md +15 -0
- graphify/cross_repo_calls.py +216 -0
- graphify/cross_repo_types.py +75 -0
- graphify/csharp_dispatch.py +154 -0
- graphify/dedup.py +1213 -0
- graphify/detect.py +2566 -0
- graphify/diagnostics.py +406 -0
- graphify/export.py +1349 -0
- graphify/exporters/__init__.py +1 -0
- graphify/exporters/base.py +14 -0
- graphify/exporters/graphdb.py +173 -0
- graphify/exporters/html.py +637 -0
- graphify/extract.py +7856 -0
- graphify/extractors/MIGRATION.md +107 -0
- graphify/extractors/__init__.py +66 -0
- graphify/extractors/apex.py +215 -0
- graphify/extractors/base.py +85 -0
- graphify/extractors/bash.py +579 -0
- graphify/extractors/blade.py +53 -0
- graphify/extractors/commonlisp.py +540 -0
- graphify/extractors/csharp.py +448 -0
- graphify/extractors/dart.py +564 -0
- graphify/extractors/dm.py +494 -0
- graphify/extractors/elixir.py +241 -0
- graphify/extractors/engine.py +6509 -0
- graphify/extractors/fortran.py +311 -0
- graphify/extractors/go.py +527 -0
- graphify/extractors/json_config.py +240 -0
- graphify/extractors/julia.py +289 -0
- graphify/extractors/markdown.py +408 -0
- graphify/extractors/models.py +131 -0
- graphify/extractors/objc.py +566 -0
- graphify/extractors/ocaml.py +289 -0
- graphify/extractors/pascal.py +688 -0
- graphify/extractors/pascal_forms.py +196 -0
- graphify/extractors/powershell.py +522 -0
- graphify/extractors/razor.py +192 -0
- graphify/extractors/resolution.py +3584 -0
- graphify/extractors/robot.py +296 -0
- graphify/extractors/rust.py +470 -0
- graphify/extractors/sln.py +92 -0
- graphify/extractors/sql.py +720 -0
- graphify/extractors/terraform.py +181 -0
- graphify/extractors/verilog.py +329 -0
- graphify/extractors/zig.py +181 -0
- graphify/file_slice.py +246 -0
- graphify/global_graph.py +194 -0
- graphify/google_workspace.py +237 -0
- graphify/hooks.py +933 -0
- graphify/ids.py +93 -0
- graphify/ingest.py +358 -0
- graphify/install.py +2366 -0
- graphify/llm.py +3544 -0
- graphify/manifest.py +4 -0
- graphify/manifest_ingest.py +311 -0
- graphify/mcp_ingest.py +386 -0
- graphify/multigraph_compat.py +212 -0
- graphify/pascal_resolution.py +129 -0
- graphify/paths.py +436 -0
- graphify/pg_introspect.py +165 -0
- graphify/prs.py +770 -0
- graphify/querylog.py +80 -0
- graphify/reflect.py +882 -0
- graphify/report.py +346 -0
- graphify/resolver_registry.py +85 -0
- graphify/ruby_resolution.py +242 -0
- graphify/scip_ingest.py +363 -0
- graphify/security.py +460 -0
- graphify/semantic_cleanup.py +336 -0
- graphify/serve.py +2608 -0
- graphify/skill-agents.md +710 -0
- graphify/skill-aider.md +1283 -0
- graphify/skill-amp.md +710 -0
- graphify/skill-claw.md +713 -0
- graphify/skill-codex.md +710 -0
- graphify/skill-copilot.md +713 -0
- graphify/skill-devin.md +1410 -0
- graphify/skill-droid.md +710 -0
- graphify/skill-kilo.md +722 -0
- graphify/skill-kiro.md +713 -0
- graphify/skill-opencode.md +705 -0
- graphify/skill-pi.md +713 -0
- graphify/skill-trae.md +711 -0
- graphify/skill-vscode.md +709 -0
- graphify/skill-windows.md +755 -0
- graphify/skill.md +713 -0
- graphify/skills/agents/references/add-watch.md +56 -0
- graphify/skills/agents/references/exports.md +87 -0
- graphify/skills/agents/references/extraction-spec.md +70 -0
- graphify/skills/agents/references/github-and-merge.md +46 -0
- graphify/skills/agents/references/hooks.md +33 -0
- graphify/skills/agents/references/query.md +311 -0
- graphify/skills/agents/references/transcribe.md +52 -0
- graphify/skills/agents/references/update.md +210 -0
- graphify/skills/amp/references/add-watch.md +56 -0
- graphify/skills/amp/references/exports.md +87 -0
- graphify/skills/amp/references/extraction-spec.md +70 -0
- graphify/skills/amp/references/github-and-merge.md +46 -0
- graphify/skills/amp/references/hooks.md +33 -0
- graphify/skills/amp/references/query.md +311 -0
- graphify/skills/amp/references/transcribe.md +52 -0
- graphify/skills/amp/references/update.md +210 -0
- graphify/skills/claude/references/add-watch.md +56 -0
- graphify/skills/claude/references/exports.md +87 -0
- graphify/skills/claude/references/extraction-spec.md +70 -0
- graphify/skills/claude/references/github-and-merge.md +46 -0
- graphify/skills/claude/references/hooks.md +33 -0
- graphify/skills/claude/references/query.md +311 -0
- graphify/skills/claude/references/transcribe.md +52 -0
- graphify/skills/claude/references/update.md +210 -0
- graphify/skills/claw/references/add-watch.md +56 -0
- graphify/skills/claw/references/exports.md +87 -0
- graphify/skills/claw/references/extraction-spec.md +31 -0
- graphify/skills/claw/references/github-and-merge.md +46 -0
- graphify/skills/claw/references/hooks.md +33 -0
- graphify/skills/claw/references/query.md +311 -0
- graphify/skills/claw/references/transcribe.md +52 -0
- graphify/skills/claw/references/update.md +210 -0
- graphify/skills/codex/references/add-watch.md +56 -0
- graphify/skills/codex/references/exports.md +87 -0
- graphify/skills/codex/references/extraction-spec.md +31 -0
- graphify/skills/codex/references/github-and-merge.md +46 -0
- graphify/skills/codex/references/hooks.md +33 -0
- graphify/skills/codex/references/query.md +311 -0
- graphify/skills/codex/references/transcribe.md +52 -0
- graphify/skills/codex/references/update.md +210 -0
- graphify/skills/copilot/references/add-watch.md +56 -0
- graphify/skills/copilot/references/exports.md +87 -0
- graphify/skills/copilot/references/extraction-spec.md +70 -0
- graphify/skills/copilot/references/github-and-merge.md +46 -0
- graphify/skills/copilot/references/hooks.md +33 -0
- graphify/skills/copilot/references/query.md +311 -0
- graphify/skills/copilot/references/transcribe.md +52 -0
- graphify/skills/copilot/references/update.md +210 -0
- graphify/skills/droid/references/add-watch.md +56 -0
- graphify/skills/droid/references/exports.md +87 -0
- graphify/skills/droid/references/extraction-spec.md +70 -0
- graphify/skills/droid/references/github-and-merge.md +46 -0
- graphify/skills/droid/references/hooks.md +33 -0
- graphify/skills/droid/references/query.md +311 -0
- graphify/skills/droid/references/transcribe.md +52 -0
- graphify/skills/droid/references/update.md +210 -0
- graphify/skills/kilo/references/add-watch.md +56 -0
- graphify/skills/kilo/references/exports.md +87 -0
- graphify/skills/kilo/references/extraction-spec.md +70 -0
- graphify/skills/kilo/references/github-and-merge.md +46 -0
- graphify/skills/kilo/references/hooks.md +33 -0
- graphify/skills/kilo/references/query.md +311 -0
- graphify/skills/kilo/references/transcribe.md +52 -0
- graphify/skills/kilo/references/update.md +210 -0
- graphify/skills/kiro/references/add-watch.md +56 -0
- graphify/skills/kiro/references/exports.md +87 -0
- graphify/skills/kiro/references/extraction-spec.md +31 -0
- graphify/skills/kiro/references/github-and-merge.md +46 -0
- graphify/skills/kiro/references/hooks.md +33 -0
- graphify/skills/kiro/references/query.md +311 -0
- graphify/skills/kiro/references/transcribe.md +52 -0
- graphify/skills/kiro/references/update.md +210 -0
- graphify/skills/opencode/references/add-watch.md +56 -0
- graphify/skills/opencode/references/exports.md +87 -0
- graphify/skills/opencode/references/extraction-spec.md +70 -0
- graphify/skills/opencode/references/github-and-merge.md +46 -0
- graphify/skills/opencode/references/hooks.md +33 -0
- graphify/skills/opencode/references/query.md +311 -0
- graphify/skills/opencode/references/transcribe.md +52 -0
- graphify/skills/opencode/references/update.md +210 -0
- graphify/skills/pi/references/add-watch.md +56 -0
- graphify/skills/pi/references/exports.md +87 -0
- graphify/skills/pi/references/extraction-spec.md +31 -0
- graphify/skills/pi/references/github-and-merge.md +46 -0
- graphify/skills/pi/references/hooks.md +33 -0
- graphify/skills/pi/references/query.md +311 -0
- graphify/skills/pi/references/transcribe.md +52 -0
- graphify/skills/pi/references/update.md +210 -0
- graphify/skills/trae/references/add-watch.md +56 -0
- graphify/skills/trae/references/exports.md +87 -0
- graphify/skills/trae/references/extraction-spec.md +70 -0
- graphify/skills/trae/references/github-and-merge.md +46 -0
- graphify/skills/trae/references/hooks.md +35 -0
- graphify/skills/trae/references/query.md +311 -0
- graphify/skills/trae/references/transcribe.md +52 -0
- graphify/skills/trae/references/update.md +210 -0
- graphify/skills/vscode/references/add-watch.md +56 -0
- graphify/skills/vscode/references/exports.md +87 -0
- graphify/skills/vscode/references/extraction-spec.md +70 -0
- graphify/skills/vscode/references/github-and-merge.md +46 -0
- graphify/skills/vscode/references/hooks.md +33 -0
- graphify/skills/vscode/references/query.md +311 -0
- graphify/skills/vscode/references/transcribe.md +52 -0
- graphify/skills/vscode/references/update.md +210 -0
- graphify/skills/windows/references/add-watch.md +56 -0
- graphify/skills/windows/references/exports.md +87 -0
- graphify/skills/windows/references/extraction-spec.md +70 -0
- graphify/skills/windows/references/github-and-merge.md +46 -0
- graphify/skills/windows/references/hooks.md +33 -0
- graphify/skills/windows/references/query.md +311 -0
- graphify/skills/windows/references/transcribe.md +52 -0
- graphify/skills/windows/references/update.md +210 -0
- graphify/symbol_resolution.py +556 -0
- graphify/transcribe.py +186 -0
- graphify/tree_html.py +603 -0
- graphify/validate.py +95 -0
- graphify/watch.py +2280 -0
- graphify/wiki.py +405 -0
- graphitect/__init__.py +28 -0
- graphitect/__main__.py +4 -0
- graphitect/_vendor/__init__.py +2 -0
- graphitect/_vendor/archify/LICENSE +22 -0
- graphitect/_vendor/archify/SKILL.md +137 -0
- graphitect/_vendor/archify/THIRD_PARTY_NOTICES.md +69 -0
- graphitect/_vendor/archify/assets/JetBrainsMono-OFL.txt +93 -0
- graphitect/_vendor/archify/assets/template.html +14935 -0
- graphitect/_vendor/archify/bin/archify.mjs +2091 -0
- graphitect/_vendor/archify/bin/open-artifact.mjs +86 -0
- graphitect/_vendor/archify/bin/preview.mjs +653 -0
- graphitect/_vendor/archify/bin/visual-check.mjs +829 -0
- graphitect/_vendor/archify/brand-marks/README.md +31 -0
- graphitect/_vendor/archify/brand-marks/catalog.json +131 -0
- graphitect/_vendor/archify/delta/architecture-delta.mjs +1221 -0
- graphitect/_vendor/archify/examples/agent-run.lifecycle.json +60 -0
- graphitect/_vendor/archify/examples/agent-tool-call.workflow.json +94 -0
- graphitect/_vendor/archify/examples/async-job-roundtrip.sequence.json +61 -0
- graphitect/_vendor/archify/examples/brand-aware-delivery.architecture.json +47 -0
- graphitect/_vendor/archify/examples/cache-miss-request.sequence.json +82 -0
- graphitect/_vendor/archify/examples/checkout-platform.base.architecture.json +31 -0
- graphitect/_vendor/archify/examples/checkout-platform.head.architecture.json +31 -0
- graphitect/_vendor/archify/examples/dataflow-product-analytics.html +15045 -0
- graphitect/_vendor/archify/examples/deployment-release.lifecycle.json +49 -0
- graphitect/_vendor/archify/examples/event-stream.dataflow.json +57 -0
- graphitect/_vendor/archify/examples/incident-response.workflow.json +64 -0
- graphitect/_vendor/archify/examples/lifecycle-agent-run.html +14980 -0
- graphitect/_vendor/archify/examples/product-analytics.dataflow.json +76 -0
- graphitect/_vendor/archify/examples/production-deployment.architecture.json +71 -0
- graphitect/_vendor/archify/examples/release-delivery.workflow.json +62 -0
- graphitect/_vendor/archify/examples/sequence-cache-miss-request.html +15060 -0
- graphitect/_vendor/archify/examples/web-app-rendered.html +15009 -0
- graphitect/_vendor/archify/examples/web-app.architecture.json +46 -0
- graphitect/_vendor/archify/examples/workflow-agent-tool-call-rendered.html +15051 -0
- graphitect/_vendor/archify/migrations/workflow-v2.mjs +279 -0
- graphitect/_vendor/archify/package-lock.json +149 -0
- graphitect/_vendor/archify/package.json +39 -0
- graphitect/_vendor/archify/recipes/scenarios.mjs +391 -0
- graphitect/_vendor/archify/references/authoring-contract.md +243 -0
- graphitect/_vendor/archify/references/brand-marks.md +65 -0
- graphitect/_vendor/archify/references/delivery-contract.md +120 -0
- graphitect/_vendor/archify/references/viewer-runtime.md +45 -0
- graphitect/_vendor/archify/renderers/architecture/grid.mjs +62 -0
- graphitect/_vendor/archify/renderers/architecture/render-architecture.mjs +1078 -0
- graphitect/_vendor/archify/renderers/dataflow/README.md +104 -0
- graphitect/_vendor/archify/renderers/dataflow/render-dataflow.mjs +483 -0
- graphitect/_vendor/archify/renderers/lifecycle/README.md +115 -0
- graphitect/_vendor/archify/renderers/lifecycle/render-lifecycle.mjs +561 -0
- graphitect/_vendor/archify/renderers/sequence/README.md +114 -0
- graphitect/_vendor/archify/renderers/sequence/render-sequence.mjs +464 -0
- graphitect/_vendor/archify/renderers/shared/brand-marks.mjs +563 -0
- graphitect/_vendor/archify/renderers/shared/cli.mjs +218 -0
- graphitect/_vendor/archify/renderers/shared/desktop-readability.mjs +26 -0
- graphitect/_vendor/archify/renderers/shared/diagnostics.mjs +127 -0
- graphitect/_vendor/archify/renderers/shared/engineering-profiles.mjs +157 -0
- graphitect/_vendor/archify/renderers/shared/generated-brand-marks.mjs +2003 -0
- graphitect/_vendor/archify/renderers/shared/generated-validators.mjs +13 -0
- graphitect/_vendor/archify/renderers/shared/geometry.mjs +1423 -0
- graphitect/_vendor/archify/renderers/shared/i18n.mjs +595 -0
- graphitect/_vendor/archify/renderers/shared/layout-report.mjs +40 -0
- graphitect/_vendor/archify/renderers/shared/legend.mjs +217 -0
- graphitect/_vendor/archify/renderers/shared/output-path.mjs +340 -0
- graphitect/_vendor/archify/renderers/shared/repository-evidence.mjs +238 -0
- graphitect/_vendor/archify/renderers/shared/repository-location.mjs +58 -0
- graphitect/_vendor/archify/renderers/shared/text-fit.mjs +49 -0
- graphitect/_vendor/archify/renderers/shared/utils.mjs +232 -0
- graphitect/_vendor/archify/renderers/shared/validator.mjs +86 -0
- graphitect/_vendor/archify/renderers/workflow/README.md +223 -0
- graphitect/_vendor/archify/renderers/workflow/render-workflow.mjs +35 -0
- graphitect/_vendor/archify/renderers/workflow/workflow-compiler.mjs +4400 -0
- graphitect/_vendor/archify/renderers/workflow/workflow-migration-geometry.mjs +144 -0
- graphitect/_vendor/archify/schemas/README.md +211 -0
- graphitect/_vendor/archify/schemas/architecture.schema.json +178 -0
- graphitect/_vendor/archify/schemas/common.schema.json +115 -0
- graphitect/_vendor/archify/schemas/dataflow.schema.json +243 -0
- graphitect/_vendor/archify/schemas/lifecycle.schema.json +266 -0
- graphitect/_vendor/archify/schemas/sequence.schema.json +223 -0
- graphitect/_vendor/archify/schemas/workflow.schema.json +428 -0
- graphitect/_vendor/archify/scripts/check-render-output.mjs +836 -0
- graphitect/_vendor/archify/scripts/check-update.mjs +1667 -0
- graphitect/_vendor/archify/scripts/generate-brand-marks.mjs +141 -0
- graphitect/_vendor/archify/scripts/generate-validators.mjs +66 -0
- graphitect/_vendor/archify/scripts/render-examples.mjs +26 -0
- graphitect/_vendor/archify/scripts/update-contract.mjs +182 -0
- graphitect/_vendor/archify/skill-release.json +10 -0
- graphitect/cli.py +981 -0
- graphitect/deliver/__init__.py +5 -0
- graphitect/deliver/archify_adapter.py +1877 -0
- graphitect/deliver/archify_ir.py +160 -0
- graphitect/deliver/archify_repair.py +135 -0
- graphitect/deliver/doc_compiler.py +916 -0
- graphitect/ground/__init__.py +5 -0
- graphitect/ground/describe_source.py +27 -0
- graphitect/ground/fullread_source.py +56 -0
- graphitect/ground/graphify_source.py +107 -0
- graphitect/models.py +118 -0
- graphitect/skill/SKILL.md +80 -0
- graphitect/skill/agents/openai.yaml +4 -0
- graphitect/synthesize/__init__.py +5 -0
- graphitect/synthesize/engine.py +281 -0
- graphitect/synthesize/llm_backend.py +331 -0
- graphitect/synthesize/questions.py +139 -0
- graphitect/synthesize/rubric.py +104 -0
- graphitect-0.2.0.dist-info/METADATA +284 -0
- graphitect-0.2.0.dist-info/RECORD +336 -0
- graphitect-0.2.0.dist-info/WHEEL +5 -0
- graphitect-0.2.0.dist-info/entry_points.txt +2 -0
- graphitect-0.2.0.dist-info/licenses/LICENSE +21 -0
- graphitect-0.2.0.dist-info/licenses/LICENSE-ARCHIFY-MIT +22 -0
- graphitect-0.2.0.dist-info/licenses/LICENSE-GRAPHIFY-APACHE-2.0 +202 -0
- graphitect-0.2.0.dist-info/licenses/LICENSE-GRAPHIFY-MIT +21 -0
- graphitect-0.2.0.dist-info/licenses/NOTICE-ARCHIFY-THIRD-PARTY.md +69 -0
- graphitect-0.2.0.dist-info/licenses/NOTICE-GRAPHIFY +8 -0
- graphitect-0.2.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"""Terraform extractor. Moved verbatim from graphify/extract.py."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from graphify.extractors.base import _make_id
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
_TF_META_HEADS = frozenset({"count", "each", "self", "path", "terraform"})
|
|
10
|
+
|
|
11
|
+
def extract_terraform(path: Path) -> dict:
|
|
12
|
+
"""Extract Terraform/HCL blocks and the references between them via tree-sitter.
|
|
13
|
+
|
|
14
|
+
Nodes: resources, data sources, modules, variables, outputs, providers, and
|
|
15
|
+
locals. Edges: `contains` (file -> block), `references` (block -> the blocks
|
|
16
|
+
it interpolates, e.g. `aws_instance.web` -> `var.region`), and `depends_on`
|
|
17
|
+
(explicit dependency edges).
|
|
18
|
+
|
|
19
|
+
Node IDs are scoped by the parent directory, not the file stem, because
|
|
20
|
+
Terraform resources are module(directory)-scoped: a resource defined in
|
|
21
|
+
main.tf is referenced from other .tf files in the same directory. Directory
|
|
22
|
+
scoping lets those cross-file references resolve when per-file extractions
|
|
23
|
+
are merged (stem scoping would split a definition from its references).
|
|
24
|
+
"""
|
|
25
|
+
try:
|
|
26
|
+
import tree_sitter_hcl as tshcl
|
|
27
|
+
from tree_sitter import Language, Parser
|
|
28
|
+
except ImportError:
|
|
29
|
+
return {"nodes": [], "edges": [], "error": "tree_sitter_hcl not installed. Run: pip install tree-sitter-hcl"}
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
language = Language(tshcl.language())
|
|
33
|
+
parser = Parser(language)
|
|
34
|
+
source = path.read_bytes()
|
|
35
|
+
tree = parser.parse(source)
|
|
36
|
+
root = tree.root_node
|
|
37
|
+
except Exception as e:
|
|
38
|
+
return {"nodes": [], "edges": [], "error": str(e)}
|
|
39
|
+
|
|
40
|
+
str_path = str(path)
|
|
41
|
+
file_nid = _make_id(str_path)
|
|
42
|
+
scope = path.parent.name or "tf"
|
|
43
|
+
|
|
44
|
+
nodes: list[dict] = [{"id": file_nid, "label": path.name, "file_type": "code",
|
|
45
|
+
"source_file": str_path, "source_location": None}]
|
|
46
|
+
edges: list[dict] = []
|
|
47
|
+
seen_ids: set[str] = {file_nid}
|
|
48
|
+
seen_edges: set[tuple[str, str, str]] = set()
|
|
49
|
+
|
|
50
|
+
def _read(n) -> str:
|
|
51
|
+
return source[n.start_byte:n.end_byte].decode("utf-8", errors="replace")
|
|
52
|
+
|
|
53
|
+
def _label_text(n) -> str:
|
|
54
|
+
return _read(n).strip().strip('"')
|
|
55
|
+
|
|
56
|
+
def _add_node(address: str, label: str, line: int) -> str:
|
|
57
|
+
nid = _make_id(scope, address)
|
|
58
|
+
if nid not in seen_ids:
|
|
59
|
+
seen_ids.add(nid)
|
|
60
|
+
nodes.append({"id": nid, "label": label, "file_type": "code",
|
|
61
|
+
"source_file": str_path, "source_location": f"L{line}"})
|
|
62
|
+
edges.append({"source": file_nid, "target": nid, "relation": "contains",
|
|
63
|
+
"confidence": "EXTRACTED", "source_file": str_path,
|
|
64
|
+
"source_location": f"L{line}", "weight": 1.0})
|
|
65
|
+
return nid
|
|
66
|
+
|
|
67
|
+
def _add_edge(src: str, address: str, relation: str, line: int) -> None:
|
|
68
|
+
tgt = _make_id(scope, address)
|
|
69
|
+
if src == tgt:
|
|
70
|
+
return
|
|
71
|
+
key = (src, tgt, relation)
|
|
72
|
+
if key in seen_edges:
|
|
73
|
+
return
|
|
74
|
+
seen_edges.add(key)
|
|
75
|
+
edges.append({"source": src, "target": tgt, "relation": relation,
|
|
76
|
+
"confidence": "EXTRACTED", "source_file": str_path,
|
|
77
|
+
"source_location": f"L{line}", "weight": 1.0})
|
|
78
|
+
|
|
79
|
+
def _block_parts(block) -> tuple:
|
|
80
|
+
btype = None
|
|
81
|
+
labels: list[str] = []
|
|
82
|
+
for c in block.children:
|
|
83
|
+
if c.type in ("block_start", "body", "block_end"):
|
|
84
|
+
break
|
|
85
|
+
if c.type == "identifier" and btype is None:
|
|
86
|
+
btype = _read(c)
|
|
87
|
+
elif c.type in ("string_lit", "identifier"):
|
|
88
|
+
labels.append(_label_text(c))
|
|
89
|
+
return btype, labels
|
|
90
|
+
|
|
91
|
+
def _ref_address(expr):
|
|
92
|
+
head = _read(expr)
|
|
93
|
+
parent = expr.parent
|
|
94
|
+
attrs: list[str] = []
|
|
95
|
+
if parent is not None:
|
|
96
|
+
seen_self = False
|
|
97
|
+
for c in parent.children:
|
|
98
|
+
if c.id == expr.id:
|
|
99
|
+
seen_self = True
|
|
100
|
+
continue
|
|
101
|
+
if seen_self and c.type == "get_attr":
|
|
102
|
+
name = None
|
|
103
|
+
for gc in c.children:
|
|
104
|
+
if gc.type == "identifier":
|
|
105
|
+
name = _read(gc)
|
|
106
|
+
break
|
|
107
|
+
if name is None:
|
|
108
|
+
break
|
|
109
|
+
attrs.append(name)
|
|
110
|
+
elif seen_self and c.type not in ("get_attr",):
|
|
111
|
+
break
|
|
112
|
+
if head in _TF_META_HEADS or not head:
|
|
113
|
+
return None
|
|
114
|
+
if head == "var":
|
|
115
|
+
return f"var.{attrs[0]}" if attrs else None
|
|
116
|
+
if head == "local":
|
|
117
|
+
return f"local.{attrs[0]}" if attrs else None
|
|
118
|
+
if head == "module":
|
|
119
|
+
return f"module.{attrs[0]}" if attrs else None
|
|
120
|
+
if head == "data":
|
|
121
|
+
return f"data.{attrs[0]}.{attrs[1]}" if len(attrs) >= 2 else None
|
|
122
|
+
return f"{head}.{attrs[0]}" if attrs else None
|
|
123
|
+
|
|
124
|
+
def _collect_refs(node, owner_nid: str, relation: str) -> None:
|
|
125
|
+
rel = relation
|
|
126
|
+
if node.type == "attribute":
|
|
127
|
+
key_node = node.child_by_field_name("key") or (
|
|
128
|
+
node.children[0] if node.children else None
|
|
129
|
+
)
|
|
130
|
+
if key_node is not None and _read(key_node) == "depends_on":
|
|
131
|
+
rel = "depends_on"
|
|
132
|
+
if node.type == "variable_expr":
|
|
133
|
+
addr = _ref_address(node)
|
|
134
|
+
if addr:
|
|
135
|
+
_add_edge(owner_nid, addr, rel, node.start_point[0] + 1)
|
|
136
|
+
for c in node.children:
|
|
137
|
+
if c.is_named:
|
|
138
|
+
_collect_refs(c, owner_nid, rel)
|
|
139
|
+
|
|
140
|
+
def _body_of(block):
|
|
141
|
+
for c in block.children:
|
|
142
|
+
if c.type == "body":
|
|
143
|
+
return c
|
|
144
|
+
return None
|
|
145
|
+
|
|
146
|
+
body = next((c for c in root.children if c.type == "body"), root)
|
|
147
|
+
for block in body.children:
|
|
148
|
+
if block.type != "block":
|
|
149
|
+
continue
|
|
150
|
+
btype, labels = _block_parts(block)
|
|
151
|
+
line = block.start_point[0] + 1
|
|
152
|
+
blk_body = _body_of(block)
|
|
153
|
+
if btype == "resource" and len(labels) >= 2:
|
|
154
|
+
owner = _add_node(f"{labels[0]}.{labels[1]}", f"{labels[0]}.{labels[1]}", line)
|
|
155
|
+
elif btype == "data" and len(labels) >= 2:
|
|
156
|
+
owner = _add_node(f"data.{labels[0]}.{labels[1]}", f"data.{labels[0]}.{labels[1]}", line)
|
|
157
|
+
elif btype == "module" and labels:
|
|
158
|
+
owner = _add_node(f"module.{labels[0]}", f"module.{labels[0]}", line)
|
|
159
|
+
elif btype == "variable" and labels:
|
|
160
|
+
owner = _add_node(f"var.{labels[0]}", f"var.{labels[0]}", line)
|
|
161
|
+
elif btype == "output" and labels:
|
|
162
|
+
owner = _add_node(f"output.{labels[0]}", f"output.{labels[0]}", line)
|
|
163
|
+
elif btype == "provider" and labels:
|
|
164
|
+
owner = _add_node(f"provider.{labels[0]}", f"provider.{labels[0]}", line)
|
|
165
|
+
elif btype == "locals" and blk_body is not None:
|
|
166
|
+
for attr in blk_body.children:
|
|
167
|
+
if attr.type != "attribute":
|
|
168
|
+
continue
|
|
169
|
+
key_node = attr.children[0] if attr.children else None
|
|
170
|
+
if key_node is None:
|
|
171
|
+
continue
|
|
172
|
+
key = _read(key_node)
|
|
173
|
+
lnid = _add_node(f"local.{key}", f"local.{key}", attr.start_point[0] + 1)
|
|
174
|
+
_collect_refs(attr, lnid, "references")
|
|
175
|
+
continue
|
|
176
|
+
else:
|
|
177
|
+
continue
|
|
178
|
+
if blk_body is not None:
|
|
179
|
+
_collect_refs(blk_body, owner, "references")
|
|
180
|
+
|
|
181
|
+
return {"nodes": nodes, "edges": edges}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
"""Verilog extractor. Moved verbatim from graphify/extract.py."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from graphify.extractors.base import _file_stem, _make_id, _read_text
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _sv_first_identifier(node, source: bytes) -> str | None:
|
|
11
|
+
"""First `simple_identifier` under node in pre-order, or None.
|
|
12
|
+
|
|
13
|
+
tree-sitter-verilog 1.0.3 nests declaration names a few levels deep instead
|
|
14
|
+
of exposing a `name` field. Scope the search to the right child node (e.g.
|
|
15
|
+
`function_identifier`) or this returns the return-type instead of the name.
|
|
16
|
+
"""
|
|
17
|
+
if node is None:
|
|
18
|
+
return None
|
|
19
|
+
for child in node.children:
|
|
20
|
+
if child.type == "simple_identifier":
|
|
21
|
+
return _read_text(child, source)
|
|
22
|
+
found = _sv_first_identifier(child, source)
|
|
23
|
+
if found:
|
|
24
|
+
return found
|
|
25
|
+
return None
|
|
26
|
+
|
|
27
|
+
def _sv_child(node, type_name: str) -> object | None:
|
|
28
|
+
if node is None:
|
|
29
|
+
return None
|
|
30
|
+
for child in node.children:
|
|
31
|
+
if child.type == type_name:
|
|
32
|
+
return child
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
_SV_BUILTIN_TYPES = frozenset({
|
|
36
|
+
"bit", "logic", "reg", "wire", "int", "integer", "shortint", "longint",
|
|
37
|
+
"byte", "time", "real", "shortreal", "void", "string", "type", "event",
|
|
38
|
+
"mailbox", "semaphore", "process", "chandle",
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
_SV_NON_TYPE_WORDS = frozenset({
|
|
42
|
+
"return", "if", "else", "for", "foreach", "while", "case", "begin", "end",
|
|
43
|
+
"function", "task", "class", "endclass", "endfunction", "endtask",
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
_SV_PARENS_INNER = r"(?:[^()]|\([^()]*\))*"
|
|
47
|
+
|
|
48
|
+
_SV_PARENS = r"\(" + _SV_PARENS_INNER + r"\)"
|
|
49
|
+
|
|
50
|
+
_SV_FUNC_RE = re.compile(
|
|
51
|
+
r"\bfunction\s+([A-Za-z_]\w*(?:\s*#\s*" + _SV_PARENS + r")?)\s+(\w+)\s*"
|
|
52
|
+
r"\((" + _SV_PARENS_INNER + r")\)\s*;",
|
|
53
|
+
re.MULTILINE,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
_SV_PARAM_RE = re.compile(
|
|
57
|
+
r"\s*(?:input|output|inout|ref|const\s+ref)?\s*"
|
|
58
|
+
r"([A-Za-z_]\w*(?:\s*#\s*" + _SV_PARENS + r")?)\s+\w+"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
def _sv_strip_comments(text: str) -> str:
|
|
62
|
+
text = re.sub(r"/\*.*?\*/", "", text, flags=re.DOTALL)
|
|
63
|
+
return re.sub(r"//.*", "", text)
|
|
64
|
+
|
|
65
|
+
def _sv_split_type_list(text: str) -> list[str]:
|
|
66
|
+
parts: list[str] = []
|
|
67
|
+
depth = 0
|
|
68
|
+
start = 0
|
|
69
|
+
for idx, ch in enumerate(text):
|
|
70
|
+
if ch == "(":
|
|
71
|
+
depth += 1
|
|
72
|
+
elif ch == ")":
|
|
73
|
+
depth = max(0, depth - 1)
|
|
74
|
+
elif ch == "," and depth == 0:
|
|
75
|
+
item = text[start:idx].strip()
|
|
76
|
+
if item:
|
|
77
|
+
parts.append(item)
|
|
78
|
+
start = idx + 1
|
|
79
|
+
item = text[start:].strip()
|
|
80
|
+
if item:
|
|
81
|
+
parts.append(item)
|
|
82
|
+
return parts
|
|
83
|
+
|
|
84
|
+
def _sv_collect_type_refs(type_text: str, generic: bool = False,
|
|
85
|
+
skip: frozenset[str] = frozenset()) -> list[tuple[str, str]]:
|
|
86
|
+
refs: list[tuple[str, str]] = []
|
|
87
|
+
text = type_text.strip()
|
|
88
|
+
if not text:
|
|
89
|
+
return refs
|
|
90
|
+
head = re.match(r"([A-Za-z_]\w*)", text)
|
|
91
|
+
if head:
|
|
92
|
+
name = head.group(1)
|
|
93
|
+
# `skip` carries the enclosing class's `#(type T = ...)` parameters so
|
|
94
|
+
# they are not mistaken for referenced types.
|
|
95
|
+
if name not in _SV_BUILTIN_TYPES and name not in _SV_NON_TYPE_WORDS and name not in skip:
|
|
96
|
+
refs.append((name, "generic_arg" if generic else "type"))
|
|
97
|
+
params = re.search(r"#\s*\((" + _SV_PARENS_INNER + r")\)", text)
|
|
98
|
+
if params:
|
|
99
|
+
for arg in _sv_split_type_list(params.group(1)):
|
|
100
|
+
refs.extend(_sv_collect_type_refs(arg, generic=True, skip=skip))
|
|
101
|
+
return refs
|
|
102
|
+
|
|
103
|
+
def _augment_systemverilog_semantics(
|
|
104
|
+
raw: str,
|
|
105
|
+
stem: str,
|
|
106
|
+
str_path: str,
|
|
107
|
+
file_nid: str,
|
|
108
|
+
nodes: list[dict],
|
|
109
|
+
edges: list[dict],
|
|
110
|
+
seen_ids: set[str],
|
|
111
|
+
) -> None:
|
|
112
|
+
label_to_nid = {node["label"]: node["id"] for node in nodes}
|
|
113
|
+
|
|
114
|
+
def line_for(offset: int) -> int:
|
|
115
|
+
return raw.count("\n", 0, offset) + 1
|
|
116
|
+
|
|
117
|
+
def add_node(nid: str, label: str, line: int) -> None:
|
|
118
|
+
if nid not in seen_ids:
|
|
119
|
+
seen_ids.add(nid)
|
|
120
|
+
nodes.append({"id": nid, "label": label, "file_type": "code",
|
|
121
|
+
"source_file": str_path, "source_location": f"L{line}",
|
|
122
|
+
"confidence_score": 1.0})
|
|
123
|
+
label_to_nid[label] = nid
|
|
124
|
+
|
|
125
|
+
def ensure_type(label: str, line: int) -> str:
|
|
126
|
+
if label in label_to_nid:
|
|
127
|
+
return label_to_nid[label]
|
|
128
|
+
nid = _make_id(stem, label)
|
|
129
|
+
add_node(nid, label, line)
|
|
130
|
+
return nid
|
|
131
|
+
|
|
132
|
+
def add_edge(src: str, target_label: str, relation: str, line: int, context: str | None = None) -> None:
|
|
133
|
+
tgt = ensure_type(target_label, line)
|
|
134
|
+
edge = {"source": src, "target": tgt, "relation": relation,
|
|
135
|
+
"confidence": "EXTRACTED", "confidence_score": 1.0,
|
|
136
|
+
"source_file": str_path, "source_location": f"L{line}", "weight": 1.0}
|
|
137
|
+
if context:
|
|
138
|
+
edge["context"] = context
|
|
139
|
+
edges.append(edge)
|
|
140
|
+
|
|
141
|
+
text = _sv_strip_comments(raw)
|
|
142
|
+
# Consuming `endclass` (rather than a lookahead) makes each match own its
|
|
143
|
+
# terminator, so back-to-back or malformed classes cannot bleed bodies.
|
|
144
|
+
class_re = re.compile(
|
|
145
|
+
r"\b(?:(interface)\s+)?class\s+(\w+)([^;{]*)\s*;(.*?)\bendclass\b",
|
|
146
|
+
re.DOTALL,
|
|
147
|
+
)
|
|
148
|
+
for match in class_re.finditer(text):
|
|
149
|
+
class_name = match.group(2)
|
|
150
|
+
header = match.group(3) or ""
|
|
151
|
+
body = match.group(4) or ""
|
|
152
|
+
line = line_for(match.start())
|
|
153
|
+
# `#(type T = Payload)` declares `T` as a class type parameter, not a
|
|
154
|
+
# referenced type — collect these to skip below.
|
|
155
|
+
type_params = frozenset(re.findall(r"\btype\s+(\w+)", header))
|
|
156
|
+
class_nid = _make_id(stem, class_name)
|
|
157
|
+
add_node(class_nid, class_name, line)
|
|
158
|
+
edges.append({"source": file_nid, "target": class_nid, "relation": "defines",
|
|
159
|
+
"confidence": "EXTRACTED", "confidence_score": 1.0,
|
|
160
|
+
"source_file": str_path, "source_location": f"L{line}", "weight": 1.0})
|
|
161
|
+
|
|
162
|
+
ext = re.search(r"\bextends\s+(\w+)", header)
|
|
163
|
+
if ext:
|
|
164
|
+
add_edge(class_nid, ext.group(1), "inherits", line)
|
|
165
|
+
impl = re.search(r"\bimplements\s+([^;{]+)", header)
|
|
166
|
+
if impl:
|
|
167
|
+
for iface_name in _sv_split_type_list(impl.group(1)):
|
|
168
|
+
add_edge(class_nid, iface_name.split("#", 1)[0].strip(), "implements", line)
|
|
169
|
+
|
|
170
|
+
body_without_functions = re.sub(
|
|
171
|
+
r"\bfunction\b.*?\bendfunction\b",
|
|
172
|
+
lambda m: "\n" * m.group(0).count("\n"),
|
|
173
|
+
body,
|
|
174
|
+
flags=re.DOTALL,
|
|
175
|
+
)
|
|
176
|
+
# Optional leading class-property qualifiers (rand/local/protected/etc.)
|
|
177
|
+
# must be consumed: otherwise a qualified field like `rand Config x;`
|
|
178
|
+
# (three tokens) fails the `<type> <name>;` shape and its type reference
|
|
179
|
+
# is silently dropped.
|
|
180
|
+
for field in re.finditer(r"^\s*(?:(?:rand|randc|local|protected|static|const|automatic|var)\s+)*([A-Za-z_]\w*(?:\s*#\s*\([^;]+?\))?)\s+\w+\s*;", body_without_functions, re.MULTILINE):
|
|
181
|
+
# Count to the start of the type token (group 1), not the match
|
|
182
|
+
# start: `^\s*` consumes the leading newline(s), so field.start()
|
|
183
|
+
# would resolve to the class's line instead of the field's.
|
|
184
|
+
field_line = line + body_without_functions.count("\n", 0, field.start(1))
|
|
185
|
+
for ref_name, role in _sv_collect_type_refs(field.group(1), skip=type_params):
|
|
186
|
+
add_edge(class_nid, ref_name, "references", field_line, "generic_arg" if role == "generic_arg" else "field")
|
|
187
|
+
|
|
188
|
+
for fm in _SV_FUNC_RE.finditer(body):
|
|
189
|
+
return_type, func_name, params = fm.group(1), fm.group(2), fm.group(3)
|
|
190
|
+
func_line = line + body.count("\n", 0, fm.start())
|
|
191
|
+
func_nid = _make_id(class_nid, func_name)
|
|
192
|
+
add_node(func_nid, func_name, func_line)
|
|
193
|
+
edges.append({"source": class_nid, "target": func_nid, "relation": "method",
|
|
194
|
+
"confidence": "EXTRACTED", "confidence_score": 1.0,
|
|
195
|
+
"source_file": str_path, "source_location": f"L{func_line}", "weight": 1.0})
|
|
196
|
+
for ref_name, role in _sv_collect_type_refs(return_type, skip=type_params):
|
|
197
|
+
add_edge(func_nid, ref_name, "references", func_line, "generic_arg" if role == "generic_arg" else "return_type")
|
|
198
|
+
for param in _sv_split_type_list(params):
|
|
199
|
+
pm = _SV_PARAM_RE.match(param)
|
|
200
|
+
if not pm:
|
|
201
|
+
continue
|
|
202
|
+
for ref_name, role in _sv_collect_type_refs(pm.group(1), skip=type_params):
|
|
203
|
+
add_edge(func_nid, ref_name, "references", func_line, "generic_arg" if role == "generic_arg" else "parameter_type")
|
|
204
|
+
|
|
205
|
+
def extract_verilog(path: Path) -> dict:
|
|
206
|
+
"""Extract modules, functions, tasks, package imports, instantiations, and
|
|
207
|
+
SystemVerilog class semantics (inherits/implements edges, field/parameter/
|
|
208
|
+
return-type references) from .v/.sv files."""
|
|
209
|
+
try:
|
|
210
|
+
import tree_sitter_verilog as tsverilog
|
|
211
|
+
from tree_sitter import Language, Parser
|
|
212
|
+
except ImportError:
|
|
213
|
+
return {"nodes": [], "edges": [], "error": "tree_sitter_verilog not installed"}
|
|
214
|
+
|
|
215
|
+
try:
|
|
216
|
+
language = Language(tsverilog.language())
|
|
217
|
+
parser = Parser(language)
|
|
218
|
+
source = path.read_bytes()
|
|
219
|
+
tree = parser.parse(source)
|
|
220
|
+
root = tree.root_node
|
|
221
|
+
except Exception as e:
|
|
222
|
+
return {"nodes": [], "edges": [], "error": str(e)}
|
|
223
|
+
|
|
224
|
+
stem = _file_stem(path)
|
|
225
|
+
str_path = str(path)
|
|
226
|
+
nodes: list[dict] = []
|
|
227
|
+
edges: list[dict] = []
|
|
228
|
+
seen_ids: set[str] = set()
|
|
229
|
+
|
|
230
|
+
def add_node(nid: str, label: str, line: int) -> None:
|
|
231
|
+
if nid not in seen_ids:
|
|
232
|
+
seen_ids.add(nid)
|
|
233
|
+
nodes.append({"id": nid, "label": label, "file_type": "code",
|
|
234
|
+
"source_file": str_path, "source_location": f"L{line}",
|
|
235
|
+
"confidence_score": 1.0})
|
|
236
|
+
|
|
237
|
+
def add_edge(src: str, tgt: str, relation: str, line: int,
|
|
238
|
+
confidence: str = "EXTRACTED", score: float = 1.0) -> None:
|
|
239
|
+
edges.append({"source": src, "target": tgt, "relation": relation,
|
|
240
|
+
"confidence": confidence, "confidence_score": score,
|
|
241
|
+
"source_file": str_path, "source_location": f"L{line}", "weight": 1.0})
|
|
242
|
+
|
|
243
|
+
file_nid = _make_id(str(path))
|
|
244
|
+
add_node(file_nid, path.name, 1)
|
|
245
|
+
|
|
246
|
+
def walk(node, module_nid: str | None = None) -> None:
|
|
247
|
+
t = node.type
|
|
248
|
+
|
|
249
|
+
# SystemVerilog class bodies are handled by _augment_systemverilog_semantics
|
|
250
|
+
# (regex over source text). Skip their subtrees so in-class methods are not
|
|
251
|
+
# double-emitted here — and with the wrong, return-type-derived name.
|
|
252
|
+
if t in ("class_declaration", "interface_class_declaration"):
|
|
253
|
+
return
|
|
254
|
+
|
|
255
|
+
if t == "module_declaration":
|
|
256
|
+
mod_name = _sv_first_identifier(_sv_child(node, "module_header"), source)
|
|
257
|
+
if mod_name:
|
|
258
|
+
line = node.start_point[0] + 1
|
|
259
|
+
nid = _make_id(stem, mod_name)
|
|
260
|
+
add_node(nid, mod_name, line)
|
|
261
|
+
add_edge(file_nid, nid, "defines", line)
|
|
262
|
+
for child in node.children:
|
|
263
|
+
walk(child, nid)
|
|
264
|
+
return
|
|
265
|
+
|
|
266
|
+
# `function_prototype` only appears inside class/interface-class bodies
|
|
267
|
+
# (skipped above) and nests its name differently; it is intentionally not
|
|
268
|
+
# handled here.
|
|
269
|
+
elif t == "function_declaration":
|
|
270
|
+
fn_body = _sv_child(node, "function_body_declaration")
|
|
271
|
+
func_name = _sv_first_identifier(_sv_child(fn_body, "function_identifier"), source)
|
|
272
|
+
if func_name:
|
|
273
|
+
line = node.start_point[0] + 1
|
|
274
|
+
parent = module_nid or file_nid
|
|
275
|
+
nid = _make_id(parent, func_name)
|
|
276
|
+
add_node(nid, f"{func_name}()", line)
|
|
277
|
+
add_edge(parent, nid, "contains", line)
|
|
278
|
+
|
|
279
|
+
elif t == "task_declaration":
|
|
280
|
+
tk_body = _sv_child(node, "task_body_declaration")
|
|
281
|
+
task_name = _sv_first_identifier(_sv_child(tk_body, "task_identifier"), source)
|
|
282
|
+
if task_name:
|
|
283
|
+
line = node.start_point[0] + 1
|
|
284
|
+
parent = module_nid or file_nid
|
|
285
|
+
nid = _make_id(parent, task_name)
|
|
286
|
+
add_node(nid, task_name, line)
|
|
287
|
+
add_edge(parent, nid, "contains", line)
|
|
288
|
+
|
|
289
|
+
elif t == "package_import_declaration":
|
|
290
|
+
for child in node.children:
|
|
291
|
+
if child.type == "package_import_item":
|
|
292
|
+
pkg_text = _read_text(child, source)
|
|
293
|
+
pkg_name = pkg_text.split("::")[0].strip()
|
|
294
|
+
if pkg_name:
|
|
295
|
+
line = node.start_point[0] + 1
|
|
296
|
+
tgt_nid = _make_id(pkg_name)
|
|
297
|
+
add_node(tgt_nid, pkg_name, line)
|
|
298
|
+
src_nid = module_nid or file_nid
|
|
299
|
+
add_edge(src_nid, tgt_nid, "imports_from", line)
|
|
300
|
+
|
|
301
|
+
elif t in ("module_instantiation", "checker_instantiation"):
|
|
302
|
+
# `leaf u_leaf();` parses as checker_instantiation in 1.0.3;
|
|
303
|
+
# module_instantiation (when it occurs) exposes a `module_type` field.
|
|
304
|
+
# Both reduce to the first identifier under the node — the instantiated
|
|
305
|
+
# type, not the instance name (which appears later).
|
|
306
|
+
if module_nid:
|
|
307
|
+
type_node = node.child_by_field_name("module_type")
|
|
308
|
+
inst_type = (_read_text(type_node, source).strip() if type_node
|
|
309
|
+
else _sv_first_identifier(node, source))
|
|
310
|
+
if inst_type:
|
|
311
|
+
line = node.start_point[0] + 1
|
|
312
|
+
tgt_nid = _make_id(inst_type)
|
|
313
|
+
add_node(tgt_nid, inst_type, line)
|
|
314
|
+
add_edge(module_nid, tgt_nid, "instantiates", line)
|
|
315
|
+
|
|
316
|
+
for child in node.children:
|
|
317
|
+
walk(child, module_nid)
|
|
318
|
+
|
|
319
|
+
walk(root)
|
|
320
|
+
_augment_systemverilog_semantics(
|
|
321
|
+
source.decode("utf-8", errors="replace"),
|
|
322
|
+
stem,
|
|
323
|
+
str_path,
|
|
324
|
+
file_nid,
|
|
325
|
+
nodes,
|
|
326
|
+
edges,
|
|
327
|
+
seen_ids,
|
|
328
|
+
)
|
|
329
|
+
return {"nodes": nodes, "edges": edges}
|