uvmls 0.1.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.
Files changed (187) hide show
  1. uvmls/__init__.py +3 -0
  2. uvmls/__main__.py +10 -0
  3. uvmls/cli.py +201 -0
  4. uvmls/documents.py +28 -0
  5. uvmls/elab.py +457 -0
  6. uvmls/fixes.py +981 -0
  7. uvmls/formatting.py +225 -0
  8. uvmls/indexer.py +2060 -0
  9. uvmls/lints.py +528 -0
  10. uvmls/mcp.py +1247 -0
  11. uvmls/model.py +363 -0
  12. uvmls/naturaldocs.py +123 -0
  13. uvmls/navigation.py +1025 -0
  14. uvmls/project.py +528 -0
  15. uvmls/sarif.py +111 -0
  16. uvmls/server.py +1533 -0
  17. uvmls/shim/uvm_macros.svh +396 -0
  18. uvmls/signatures.py +130 -0
  19. uvmls/third_party/.rules.verible_lint +67 -0
  20. uvmls/third_party/uvm-1.2/LICENSE.txt +201 -0
  21. uvmls/third_party/uvm-1.2/NOTICE.txt +48 -0
  22. uvmls/third_party/uvm-1.2/README.md +20 -0
  23. uvmls/third_party/uvm-1.2/src/base/uvm_barrier.svh +209 -0
  24. uvmls/third_party/uvm-1.2/src/base/uvm_base.svh +111 -0
  25. uvmls/third_party/uvm-1.2/src/base/uvm_bottomup_phase.svh +112 -0
  26. uvmls/third_party/uvm-1.2/src/base/uvm_callback.svh +1198 -0
  27. uvmls/third_party/uvm-1.2/src/base/uvm_cmdline_processor.svh +461 -0
  28. uvmls/third_party/uvm-1.2/src/base/uvm_common_phases.svh +451 -0
  29. uvmls/third_party/uvm-1.2/src/base/uvm_comparer.svh +422 -0
  30. uvmls/third_party/uvm-1.2/src/base/uvm_component.svh +3638 -0
  31. uvmls/third_party/uvm-1.2/src/base/uvm_config_db.svh +410 -0
  32. uvmls/third_party/uvm-1.2/src/base/uvm_coreservice.svh +223 -0
  33. uvmls/third_party/uvm-1.2/src/base/uvm_domain.svh +218 -0
  34. uvmls/third_party/uvm-1.2/src/base/uvm_event.svh +394 -0
  35. uvmls/third_party/uvm-1.2/src/base/uvm_event_callback.svh +90 -0
  36. uvmls/third_party/uvm-1.2/src/base/uvm_factory.svh +1789 -0
  37. uvmls/third_party/uvm-1.2/src/base/uvm_globals.svh +564 -0
  38. uvmls/third_party/uvm-1.2/src/base/uvm_heartbeat.svh +341 -0
  39. uvmls/third_party/uvm-1.2/src/base/uvm_links.svh +370 -0
  40. uvmls/third_party/uvm-1.2/src/base/uvm_misc.svh +753 -0
  41. uvmls/third_party/uvm-1.2/src/base/uvm_object.svh +1330 -0
  42. uvmls/third_party/uvm-1.2/src/base/uvm_object_globals.svh +657 -0
  43. uvmls/third_party/uvm-1.2/src/base/uvm_objection.svh +1453 -0
  44. uvmls/third_party/uvm-1.2/src/base/uvm_packer.svh +1037 -0
  45. uvmls/third_party/uvm-1.2/src/base/uvm_phase.svh +2253 -0
  46. uvmls/third_party/uvm-1.2/src/base/uvm_pool.svh +347 -0
  47. uvmls/third_party/uvm-1.2/src/base/uvm_port_base.svh +792 -0
  48. uvmls/third_party/uvm-1.2/src/base/uvm_printer.svh +1231 -0
  49. uvmls/third_party/uvm-1.2/src/base/uvm_queue.svh +203 -0
  50. uvmls/third_party/uvm-1.2/src/base/uvm_recorder.svh +1140 -0
  51. uvmls/third_party/uvm-1.2/src/base/uvm_registry.svh +380 -0
  52. uvmls/third_party/uvm-1.2/src/base/uvm_report_catcher.svh +711 -0
  53. uvmls/third_party/uvm-1.2/src/base/uvm_report_handler.svh +799 -0
  54. uvmls/third_party/uvm-1.2/src/base/uvm_report_message.svh +939 -0
  55. uvmls/third_party/uvm-1.2/src/base/uvm_report_object.svh +658 -0
  56. uvmls/third_party/uvm-1.2/src/base/uvm_report_server.svh +921 -0
  57. uvmls/third_party/uvm-1.2/src/base/uvm_resource.svh +1694 -0
  58. uvmls/third_party/uvm-1.2/src/base/uvm_resource_db.svh +399 -0
  59. uvmls/third_party/uvm-1.2/src/base/uvm_resource_specializations.svh +170 -0
  60. uvmls/third_party/uvm-1.2/src/base/uvm_root.svh +1072 -0
  61. uvmls/third_party/uvm-1.2/src/base/uvm_runtime_phases.svh +555 -0
  62. uvmls/third_party/uvm-1.2/src/base/uvm_spell_chkr.svh +203 -0
  63. uvmls/third_party/uvm-1.2/src/base/uvm_task_phase.svh +161 -0
  64. uvmls/third_party/uvm-1.2/src/base/uvm_topdown_phase.svh +114 -0
  65. uvmls/third_party/uvm-1.2/src/base/uvm_tr_database.svh +432 -0
  66. uvmls/third_party/uvm-1.2/src/base/uvm_tr_stream.svh +591 -0
  67. uvmls/third_party/uvm-1.2/src/base/uvm_transaction.svh +780 -0
  68. uvmls/third_party/uvm-1.2/src/base/uvm_traversal.svh +288 -0
  69. uvmls/third_party/uvm-1.2/src/base/uvm_version.svh +37 -0
  70. uvmls/third_party/uvm-1.2/src/comps/uvm_agent.svh +135 -0
  71. uvmls/third_party/uvm-1.2/src/comps/uvm_algorithmic_comparator.svh +134 -0
  72. uvmls/third_party/uvm-1.2/src/comps/uvm_comps.svh +36 -0
  73. uvmls/third_party/uvm-1.2/src/comps/uvm_driver.svh +88 -0
  74. uvmls/third_party/uvm-1.2/src/comps/uvm_env.svh +53 -0
  75. uvmls/third_party/uvm-1.2/src/comps/uvm_in_order_comparator.svh +259 -0
  76. uvmls/third_party/uvm-1.2/src/comps/uvm_monitor.svh +53 -0
  77. uvmls/third_party/uvm-1.2/src/comps/uvm_pair.svh +170 -0
  78. uvmls/third_party/uvm-1.2/src/comps/uvm_policies.svh +141 -0
  79. uvmls/third_party/uvm-1.2/src/comps/uvm_push_driver.svh +96 -0
  80. uvmls/third_party/uvm-1.2/src/comps/uvm_random_stimulus.svh +131 -0
  81. uvmls/third_party/uvm-1.2/src/comps/uvm_scoreboard.svh +55 -0
  82. uvmls/third_party/uvm-1.2/src/comps/uvm_subscriber.svh +67 -0
  83. uvmls/third_party/uvm-1.2/src/comps/uvm_test.svh +81 -0
  84. uvmls/third_party/uvm-1.2/src/dap/uvm_dap.svh +34 -0
  85. uvmls/third_party/uvm-1.2/src/dap/uvm_get_to_lock_dap.svh +154 -0
  86. uvmls/third_party/uvm-1.2/src/dap/uvm_set_before_get_dap.svh +184 -0
  87. uvmls/third_party/uvm-1.2/src/dap/uvm_set_get_dap_base.svh +83 -0
  88. uvmls/third_party/uvm-1.2/src/dap/uvm_simple_lock_dap.svh +178 -0
  89. uvmls/third_party/uvm-1.2/src/deprecated/readme.important +35 -0
  90. uvmls/third_party/uvm-1.2/src/deprecated/uvm_resource_converter.svh +199 -0
  91. uvmls/third_party/uvm-1.2/src/dpi/uvm_common.c +50 -0
  92. uvmls/third_party/uvm-1.2/src/dpi/uvm_dpi.cc +43 -0
  93. uvmls/third_party/uvm-1.2/src/dpi/uvm_dpi.h +65 -0
  94. uvmls/third_party/uvm-1.2/src/dpi/uvm_dpi.svh +42 -0
  95. uvmls/third_party/uvm-1.2/src/dpi/uvm_hdl.c +36 -0
  96. uvmls/third_party/uvm-1.2/src/dpi/uvm_hdl.svh +163 -0
  97. uvmls/third_party/uvm-1.2/src/dpi/uvm_hdl_inca.c +677 -0
  98. uvmls/third_party/uvm-1.2/src/dpi/uvm_hdl_questa.c +406 -0
  99. uvmls/third_party/uvm-1.2/src/dpi/uvm_hdl_vcs.c +426 -0
  100. uvmls/third_party/uvm-1.2/src/dpi/uvm_regex.cc +256 -0
  101. uvmls/third_party/uvm-1.2/src/dpi/uvm_regex.svh +88 -0
  102. uvmls/third_party/uvm-1.2/src/dpi/uvm_svcmd_dpi.c +134 -0
  103. uvmls/third_party/uvm-1.2/src/dpi/uvm_svcmd_dpi.svh +65 -0
  104. uvmls/third_party/uvm-1.2/src/macros/uvm_callback_defines.svh +300 -0
  105. uvmls/third_party/uvm-1.2/src/macros/uvm_deprecated_defines.svh +250 -0
  106. uvmls/third_party/uvm-1.2/src/macros/uvm_global_defines.svh +59 -0
  107. uvmls/third_party/uvm-1.2/src/macros/uvm_message_defines.svh +537 -0
  108. uvmls/third_party/uvm-1.2/src/macros/uvm_object_defines.svh +3814 -0
  109. uvmls/third_party/uvm-1.2/src/macros/uvm_phase_defines.svh +129 -0
  110. uvmls/third_party/uvm-1.2/src/macros/uvm_printer_defines.svh +422 -0
  111. uvmls/third_party/uvm-1.2/src/macros/uvm_reg_defines.svh +68 -0
  112. uvmls/third_party/uvm-1.2/src/macros/uvm_sequence_defines.svh +455 -0
  113. uvmls/third_party/uvm-1.2/src/macros/uvm_tlm_defines.svh +616 -0
  114. uvmls/third_party/uvm-1.2/src/macros/uvm_undefineall.svh +85 -0
  115. uvmls/third_party/uvm-1.2/src/macros/uvm_version_defines.svh +149 -0
  116. uvmls/third_party/uvm-1.2/src/reg/sequences/uvm_mem_access_seq.svh +307 -0
  117. uvmls/third_party/uvm-1.2/src/reg/sequences/uvm_mem_walk_seq.svh +299 -0
  118. uvmls/third_party/uvm-1.2/src/reg/sequences/uvm_reg_access_seq.svh +365 -0
  119. uvmls/third_party/uvm-1.2/src/reg/sequences/uvm_reg_bit_bash_seq.svh +302 -0
  120. uvmls/third_party/uvm-1.2/src/reg/sequences/uvm_reg_hw_reset_seq.svh +171 -0
  121. uvmls/third_party/uvm-1.2/src/reg/sequences/uvm_reg_mem_built_in_seq.svh +138 -0
  122. uvmls/third_party/uvm-1.2/src/reg/sequences/uvm_reg_mem_hdl_paths_seq.svh +174 -0
  123. uvmls/third_party/uvm-1.2/src/reg/sequences/uvm_reg_mem_shared_access_seq.svh +485 -0
  124. uvmls/third_party/uvm-1.2/src/reg/uvm_mem.svh +2409 -0
  125. uvmls/third_party/uvm-1.2/src/reg/uvm_mem_mam.svh +1017 -0
  126. uvmls/third_party/uvm-1.2/src/reg/uvm_reg.svh +3101 -0
  127. uvmls/third_party/uvm-1.2/src/reg/uvm_reg_adapter.svh +253 -0
  128. uvmls/third_party/uvm-1.2/src/reg/uvm_reg_backdoor.svh +347 -0
  129. uvmls/third_party/uvm-1.2/src/reg/uvm_reg_block.svh +2271 -0
  130. uvmls/third_party/uvm-1.2/src/reg/uvm_reg_cbs.svh +529 -0
  131. uvmls/third_party/uvm-1.2/src/reg/uvm_reg_field.svh +2012 -0
  132. uvmls/third_party/uvm-1.2/src/reg/uvm_reg_fifo.svh +310 -0
  133. uvmls/third_party/uvm-1.2/src/reg/uvm_reg_file.svh +500 -0
  134. uvmls/third_party/uvm-1.2/src/reg/uvm_reg_indirect.svh +329 -0
  135. uvmls/third_party/uvm-1.2/src/reg/uvm_reg_item.svh +315 -0
  136. uvmls/third_party/uvm-1.2/src/reg/uvm_reg_map.svh +2224 -0
  137. uvmls/third_party/uvm-1.2/src/reg/uvm_reg_model.svh +442 -0
  138. uvmls/third_party/uvm-1.2/src/reg/uvm_reg_predictor.svh +264 -0
  139. uvmls/third_party/uvm-1.2/src/reg/uvm_reg_sequence.svh +547 -0
  140. uvmls/third_party/uvm-1.2/src/reg/uvm_vreg.svh +1553 -0
  141. uvmls/third_party/uvm-1.2/src/reg/uvm_vreg_field.svh +1004 -0
  142. uvmls/third_party/uvm-1.2/src/seq/uvm_push_sequencer.svh +84 -0
  143. uvmls/third_party/uvm-1.2/src/seq/uvm_seq.svh +39 -0
  144. uvmls/third_party/uvm-1.2/src/seq/uvm_sequence.svh +147 -0
  145. uvmls/third_party/uvm-1.2/src/seq/uvm_sequence_base.svh +1404 -0
  146. uvmls/third_party/uvm-1.2/src/seq/uvm_sequence_builtin.svh +299 -0
  147. uvmls/third_party/uvm-1.2/src/seq/uvm_sequence_item.svh +497 -0
  148. uvmls/third_party/uvm-1.2/src/seq/uvm_sequence_library.svh +812 -0
  149. uvmls/third_party/uvm-1.2/src/seq/uvm_sequencer.svh +346 -0
  150. uvmls/third_party/uvm-1.2/src/seq/uvm_sequencer_analysis_fifo.svh +38 -0
  151. uvmls/third_party/uvm-1.2/src/seq/uvm_sequencer_base.svh +1774 -0
  152. uvmls/third_party/uvm-1.2/src/seq/uvm_sequencer_param_base.svh +459 -0
  153. uvmls/third_party/uvm-1.2/src/tlm1/uvm_analysis_port.svh +155 -0
  154. uvmls/third_party/uvm-1.2/src/tlm1/uvm_exports.svh +259 -0
  155. uvmls/third_party/uvm-1.2/src/tlm1/uvm_imps.svh +316 -0
  156. uvmls/third_party/uvm-1.2/src/tlm1/uvm_ports.svh +262 -0
  157. uvmls/third_party/uvm-1.2/src/tlm1/uvm_sqr_connections.svh +83 -0
  158. uvmls/third_party/uvm-1.2/src/tlm1/uvm_sqr_ifs.svh +252 -0
  159. uvmls/third_party/uvm-1.2/src/tlm1/uvm_tlm.svh +39 -0
  160. uvmls/third_party/uvm-1.2/src/tlm1/uvm_tlm_fifo_base.svh +252 -0
  161. uvmls/third_party/uvm-1.2/src/tlm1/uvm_tlm_fifos.svh +238 -0
  162. uvmls/third_party/uvm-1.2/src/tlm1/uvm_tlm_ifs.svh +218 -0
  163. uvmls/third_party/uvm-1.2/src/tlm1/uvm_tlm_imps.svh +228 -0
  164. uvmls/third_party/uvm-1.2/src/tlm1/uvm_tlm_req_rsp.svh +348 -0
  165. uvmls/third_party/uvm-1.2/src/tlm2/uvm_tlm2.svh +29 -0
  166. uvmls/third_party/uvm-1.2/src/tlm2/uvm_tlm2_defines.svh +44 -0
  167. uvmls/third_party/uvm-1.2/src/tlm2/uvm_tlm2_exports.svh +64 -0
  168. uvmls/third_party/uvm-1.2/src/tlm2/uvm_tlm2_generic_payload.svh +1052 -0
  169. uvmls/third_party/uvm-1.2/src/tlm2/uvm_tlm2_ifs.svh +177 -0
  170. uvmls/third_party/uvm-1.2/src/tlm2/uvm_tlm2_imps.svh +202 -0
  171. uvmls/third_party/uvm-1.2/src/tlm2/uvm_tlm2_ports.svh +74 -0
  172. uvmls/third_party/uvm-1.2/src/tlm2/uvm_tlm2_sockets.svh +433 -0
  173. uvmls/third_party/uvm-1.2/src/tlm2/uvm_tlm2_sockets_base.svh +194 -0
  174. uvmls/third_party/uvm-1.2/src/tlm2/uvm_tlm2_time.svh +332 -0
  175. uvmls/third_party/uvm-1.2/src/uvm.sv +24 -0
  176. uvmls/third_party/uvm-1.2/src/uvm_macros.svh +81 -0
  177. uvmls/third_party/uvm-1.2/src/uvm_pkg.sv +42 -0
  178. uvmls/uvm.py +168 -0
  179. uvmls/views.py +737 -0
  180. uvmls/workspace.py +1132 -0
  181. uvmls-0.1.0.dist-info/METADATA +63 -0
  182. uvmls-0.1.0.dist-info/RECORD +187 -0
  183. uvmls-0.1.0.dist-info/WHEEL +5 -0
  184. uvmls-0.1.0.dist-info/entry_points.txt +2 -0
  185. uvmls-0.1.0.dist-info/licenses/LICENSE +201 -0
  186. uvmls-0.1.0.dist-info/licenses/NOTICE +46 -0
  187. uvmls-0.1.0.dist-info/top_level.txt +1 -0
uvmls/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """UVM-aware SystemVerilog language server built on slang (pyslang)."""
2
+
3
+ __version__ = "0.1.0"
uvmls/__main__.py ADDED
@@ -0,0 +1,10 @@
1
+ import sys
2
+
3
+ if len(sys.argv) > 1 and sys.argv[1] == "elab-worker":
4
+ from .elab import worker as main
5
+ elif len(sys.argv) > 1 and sys.argv[1] in ("graph", "topology", "configdb", "lint", "stats", "elab", "mcp"):
6
+ from .cli import main
7
+ else:
8
+ from .server import main
9
+
10
+ main()
uvmls/cli.py ADDED
@@ -0,0 +1,201 @@
1
+ """Command line access to the index, for scripts and CI.
2
+
3
+ python -m uvmls lint -f tb.f # exit 1 if any warning/error
4
+ python -m uvmls elab -f tb.f [--top T] # full slang elaboration; exit 1 if any error
5
+ python -m uvmls graph -f tb.f -o graph.json
6
+ python -m uvmls topology -f tb.f --test my_test
7
+ python -m uvmls configdb -f tb.f
8
+ python -m uvmls stats [DIR]
9
+ python -m uvmls mcp [DIR] -f tb.f # MCP server over stdio (see mcp.py)
10
+
11
+ The UVM library is --uvm-home or $UVM_HOME, else a uvm_pkg.sv the project already
12
+ compiles, else the bundled Accellera UVM 1.2 (--no-bundled-uvm: the built-in model).
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import argparse
18
+ import json
19
+ import os
20
+ import sys
21
+
22
+ from . import __version__
23
+ from .elab import displayed, elaborate, request_for, skip_reason, uvm_label
24
+ from .lints import lint
25
+ from .model import Diag, Range
26
+ from .project import ProjectConfig, build_source_set
27
+ from .sarif import write_log
28
+ from .views import class_graph, config_db_report, topology
29
+ from .workspace import Workspace
30
+
31
+
32
+ def _workspace(args, index: bool = True, config: ProjectConfig | None = None) -> Workspace:
33
+ """index=False only works out the sources (all `elab` needs with a filelist); `config` replaces
34
+ the one the flags describe."""
35
+ root = os.path.abspath(args.dir)
36
+ cfg = config or ProjectConfig(workspace_roots=[root],
37
+ filelists=[os.path.abspath(f) for f in args.filelist],
38
+ include_dirs=[os.path.abspath(d) for d in args.incdir],
39
+ defines=dict(d.partition("=")[::2] for d in args.define),
40
+ uvm_home=args.uvm_home, env_uvm_home=os.environ.get("UVM_HOME"),
41
+ bundled_uvm=not args.no_bundled_uvm, exclude=list(args.exclude))
42
+ ws = Workspace()
43
+ if index:
44
+ ws.configure(cfg)
45
+ else:
46
+ ws.config, ws.sources = cfg, build_source_set(cfg)
47
+ for w in ws.sources.warnings:
48
+ print(f"warning: {w}", file=sys.stderr)
49
+ return ws
50
+
51
+
52
+ def _print_tree(node, depth=0, out=sys.stdout):
53
+ extra = []
54
+ if node.get("overriddenFrom"):
55
+ extra.append(f"override of {node['overriddenFrom']}")
56
+ if node.get("conditional"):
57
+ extra.append(f"if {node['condition']}")
58
+ tail = f" ({'; '.join(extra)})" if extra else ""
59
+ out.write(f"{' ' * depth}{node['name']} : {node['type']} [{node['role']}]{tail}\n")
60
+ for c in node["children"]:
61
+ _print_tree(c, depth + 1, out)
62
+
63
+
64
+ def _elab(ws: Workspace, args) -> int:
65
+ """Exit status: 0 clean, 1 errors, 2 not elaborated (skipped or failed)."""
66
+ reason = skip_reason(ws)
67
+ if reason:
68
+ if args.sarif:
69
+ write_log(args.sarif, args.dir, [])
70
+ print(f"elaboration skipped: {reason}", file=sys.stderr)
71
+ return 2
72
+ request = request_for(ws, tops=args.top)
73
+ request["listAll"] = bool(args.sarif) # a code-scanning report must not drop findings past the display limits
74
+ result = elaborate(request)
75
+ if args.sarif:
76
+ write_log(args.sarif, args.dir, [Diag(
77
+ Range(d["file"] or "", d["line"], d["col"], d["endLine"], d["endCol"]),
78
+ d["message"], d["severity"], d["code"], "slang-elab") for d in result["diagnostics"]],
79
+ fallback=args.filelist[0] if args.filelist else None)
80
+ listed, more = displayed(result["diagnostics"]) # the console and --json keep their limits
81
+ result = {**result, "diagnostics": listed, "counts": {**result["counts"], "omitted": result["counts"]["omitted"] + more}}
82
+ out = sys.stderr if args.sarif == "-" else sys.stdout
83
+ if args.json and args.sarif != "-":
84
+ print(json.dumps(result, indent=1))
85
+ elif not args.json:
86
+ for d in result["diagnostics"]:
87
+ where = f"{os.path.relpath(d['file'])}:{d['line'] + 1}:{d['col'] + 1}: " if d["file"] else ""
88
+ print(f"{where}{d['severity']}: {d['message']} [{d['code']}]", file=out)
89
+ c = result["counts"]
90
+ notes = [f"top: {', '.join(result['tops']) or 'none'}", uvm_label(ws.sources)]
91
+ if c.get("omitted"):
92
+ notes.append(f"{c['omitted']} more not listed")
93
+ if c.get("libraryErrors"):
94
+ notes.append(f"{c['libraryErrors']} error(s) the UVM library reports on its own not shown")
95
+ print(f"{c['errors']} error(s), {c['warnings']} warning(s) in {result['seconds']}s "
96
+ f"({'; '.join(notes)})", file=sys.stderr)
97
+ if result["error"]:
98
+ print(f"elaboration failed: {result['error']}", file=sys.stderr)
99
+ return 2
100
+ return 1 if result["counts"]["errors"] else 0
101
+
102
+
103
+ def findings(ws: Workspace) -> list[Diag]:
104
+ """Lint findings and parse errors or warnings of the user files, each once, in file order."""
105
+ diags = [d for d in ws.user_diagnostics() if d.severity in ("error", "warning")]
106
+ for ds in lint(ws).values():
107
+ diags += ds
108
+ seen = set()
109
+ uniq = []
110
+ for d in sorted(diags, key=lambda d: (d.range.path, d.range.line, d.range.col)):
111
+ key = (d.range.path, d.range.line, d.range.col, d.message)
112
+ if key not in seen:
113
+ seen.add(key)
114
+ uniq.append(d)
115
+ return uniq
116
+
117
+
118
+ def pyslang_version() -> str:
119
+ try:
120
+ from importlib.metadata import version
121
+ return version("pyslang")
122
+ except Exception: # not installed as a distribution
123
+ return "unknown"
124
+
125
+
126
+ def main(argv=None):
127
+ ap = argparse.ArgumentParser(prog="python -m uvmls", description=__doc__,
128
+ formatter_class=argparse.RawDescriptionHelpFormatter)
129
+ ap.add_argument("--version", action="version", version=f"uvmls {__version__} (pyslang {pyslang_version()})")
130
+ ap.add_argument("command", choices=["graph", "topology", "configdb", "lint", "stats", "elab", "mcp"])
131
+ ap.add_argument("dir", nargs="?", default=".", help="workspace directory (default: .)")
132
+ ap.add_argument("-f", dest="filelist", action="append", default=[], help="filelist (.f)")
133
+ ap.add_argument("-I", "--incdir", action="append", default=[], help="include directory")
134
+ ap.add_argument("-D", "--define", action="append", default=[], help="macro define NAME[=VALUE]")
135
+ ap.add_argument("--uvm-home", help="UVM installation (falls back to $UVM_HOME)")
136
+ ap.add_argument("--no-bundled-uvm", action="store_true",
137
+ help="use the built-in UVM model instead of the bundled UVM 1.2")
138
+ ap.add_argument("--exclude", action="append", default=[], metavar="GLOB",
139
+ help="leave out files matching this glob in discovery mode (as uvm.exclude)")
140
+ ap.add_argument("--test", help="test class for topology")
141
+ ap.add_argument("--top", action="append", default=[], help="top module for elab (default: slang picks)")
142
+ ap.add_argument("--json", action="store_true", help="print JSON instead of text")
143
+ ap.add_argument("--sarif", metavar="FILE", help="write lint or elab diagnostics as SARIF (use - for stdout)")
144
+ ap.add_argument("-o", "--output", help="write JSON to this file")
145
+ # Intermixed: `lint --test t DIR -f x.f` has the positional after an option, which argparse before
146
+ # Python 3.12 would otherwise take as absent and reject DIR as unrecognized.
147
+ args = ap.parse_intermixed_args(argv)
148
+ if args.sarif and args.command not in ("lint", "elab"):
149
+ ap.error("--sarif is only available for lint and elab")
150
+ if args.command == "mcp":
151
+ from .mcp import serve
152
+ sys.exit(serve(args))
153
+ # Without a filelist, elab needs the index to spot several testbenches in one tree.
154
+ ws = _workspace(args, index=not (args.command == "elab" and args.filelist))
155
+
156
+ if args.command == "elab":
157
+ sys.exit(_elab(ws, args))
158
+ if args.command == "stats":
159
+ data = ws.stats()
160
+ elif args.command == "graph":
161
+ data = class_graph(ws)
162
+ elif args.command == "topology":
163
+ data = topology(ws, args.test)
164
+ elif args.command == "configdb":
165
+ data = config_db_report(ws)
166
+ else:
167
+ uniq = findings(ws)
168
+ if args.sarif:
169
+ write_log(args.sarif, args.dir, uniq)
170
+ out = sys.stderr if args.sarif == "-" else sys.stdout
171
+ if args.json and args.sarif != "-":
172
+ print(json.dumps([{"file": d.range.path, "line": d.range.line + 1, "col": d.range.col + 1,
173
+ "severity": d.severity, "code": d.code, "message": d.message}
174
+ for d in uniq], indent=2))
175
+ elif not args.json:
176
+ for d in uniq:
177
+ rel = os.path.relpath(d.range.path)
178
+ print(f"{rel}:{d.range.line + 1}:{d.range.col + 1}: {d.severity}: {d.message} [{d.code}]", file=out)
179
+ print(f"{len(uniq)} problem(s)", file=sys.stderr)
180
+ sys.exit(1 if any(d.severity in ("error", "warning") for d in uniq) else 0)
181
+
182
+ if args.output:
183
+ with open(args.output, "w") as f:
184
+ json.dump(data, f, indent=1)
185
+ elif args.json or args.command in ("graph", "stats"):
186
+ print(json.dumps(data, indent=1))
187
+ elif args.command == "topology":
188
+ print(f"test: {data['test']} (tests: {', '.join(data['tests'])})")
189
+ if data["root"]:
190
+ _print_tree(data["root"])
191
+ for c in data["connections"]:
192
+ print(f" {c['source']}.{c['sourcePort']} -> {c['target']}.{c['targetPort']}"
193
+ + (" (alias)" if c["kind"] == "alias" else ""))
194
+ elif args.command == "configdb":
195
+ for row in data["fields"]:
196
+ print(f"{row['field']:24} {row['status']:14} types={', '.join(row['types'])} "
197
+ f"sets={len(row['sets'])} gets={len(row['gets'])}")
198
+
199
+
200
+ if __name__ == "__main__":
201
+ main()
uvmls/documents.py ADDED
@@ -0,0 +1,28 @@
1
+ """LSP documents use CR/LF line endings, including when applying incremental changes."""
2
+
3
+ import re
4
+
5
+ from pygls.workspace import Workspace as PyglsWorkspace
6
+ from pygls.workspace.text_document import TextDocument as PyglsTextDocument
7
+
8
+ LINE_RE = re.compile(r"[^\r\n]*(?:\r\n|\r|\n)|[^\r\n]+$")
9
+
10
+
11
+ class TextDocument(PyglsTextDocument):
12
+ @property
13
+ def source(self) -> str:
14
+ if self._source is None and self.path is not None:
15
+ with open(self.path, encoding="utf-8", newline="") as f:
16
+ return f.read()
17
+ return self._source or ""
18
+
19
+ @property
20
+ def lines(self) -> tuple[str, ...]:
21
+ # str.splitlines also splits page breaks and Unicode separators, unlike LSP and slang.
22
+ return tuple(LINE_RE.findall(self.source))
23
+
24
+
25
+ class Workspace(PyglsWorkspace):
26
+ def _create_text_document(self, doc_uri, source=None, version=None, language_id=None):
27
+ return TextDocument(doc_uri, source=source, version=version, language_id=language_id,
28
+ sync_kind=self._sync_kind, position_codec=self._position_codec)