diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3eb7e7ab..6db30efe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -90,6 +90,9 @@ importers: tree-sitter-rust: specifier: ^0.24.0 version: 0.24.0 + tree-sitter-systemverilog: + specifier: ^0.3.1 + version: 0.3.1 tree-sitter-typescript: specifier: ^0.23.2 version: 0.23.2 @@ -835,6 +838,10 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + '@istanbuljs/schema@0.1.3': resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} @@ -855,6 +862,14 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@npmcli/agent@3.0.0': + resolution: {integrity: sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@npmcli/fs@4.0.0': + resolution: {integrity: sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==} + engines: {node: ^18.17.0 || >=20.5.0} + '@oslojs/encoding@1.1.0': resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} @@ -1267,6 +1282,7 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + deprecated: Potential CWE-502 - Update to 1.3.1 or higher '@vitejs/plugin-react@4.7.0': resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} @@ -1321,6 +1337,10 @@ packages: '@xyflow/system@0.0.75': resolution: {integrity: sha512-iXs+AGFLi8w/VlAoc/iSxk+CxfT6o64Uw/k0CKASOPqjqz6E0rb5jFZgJtXGZCpfQI6OQpu5EnumP5fGxQheaQ==} + abbrev@3.0.1: + resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==} + engines: {node: ^18.17.0 || >=20.5.0} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1331,6 +1351,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + ajv@6.15.0: resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} @@ -1417,6 +1441,10 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + cacache@19.0.1: + resolution: {integrity: sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==} + engines: {node: ^18.17.0 || >=20.5.0} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -1455,6 +1483,10 @@ packages: resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} engines: {node: '>= 20.19.0'} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + ci-info@4.4.0: resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} engines: {node: '>=8'} @@ -1647,6 +1679,9 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + enhanced-resolve@5.20.0: resolution: {integrity: sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==} engines: {node: '>=10.13.0'} @@ -1659,6 +1694,13 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + err-code@2.0.3: + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} @@ -1753,6 +1795,9 @@ packages: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} + exponential-backoff@3.1.3: + resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} + extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -1804,6 +1849,10 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} + fs-minipass@3.0.3: + resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1927,6 +1976,18 @@ packages: http-cache-semantics@4.2.0: resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1946,6 +2007,10 @@ packages: inline-style-parser@0.2.7: resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + ip-address@10.2.0: + resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==} + engines: {node: '>= 12'} + iron-webcrypto@1.2.1: resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} @@ -1999,6 +2064,10 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isexe@3.1.5: + resolution: {integrity: sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==} + engines: {node: '>=18'} + istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -2240,6 +2309,10 @@ packages: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} + make-fetch-happen@14.0.3: + resolution: {integrity: sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==} + engines: {node: ^18.17.0 || >=20.5.0} + markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} @@ -2392,10 +2465,38 @@ packages: resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} engines: {node: '>=16 || 14 >=14.17'} + minipass-collect@2.0.1: + resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass-fetch@4.0.1: + resolution: {integrity: sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + minipass-flush@1.0.7: + resolution: {integrity: sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==} + engines: {node: '>= 8'} + + minipass-pipeline@1.2.4: + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} + + minipass-sized@1.0.3: + resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} + engines: {node: '>=8'} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + minipass@7.1.3: resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + mnemonist@0.39.8: resolution: {integrity: sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ==} @@ -2414,6 +2515,10 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + neotraverse@0.6.18: resolution: {integrity: sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==} engines: {node: '>= 10'} @@ -2432,12 +2537,22 @@ packages: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true + node-gyp@11.5.0: + resolution: {integrity: sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + node-mock-http@1.0.4: resolution: {integrity: sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==} node-releases@2.0.37: resolution: {integrity: sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==} + nopt@8.1.0: + resolution: {integrity: sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -2479,6 +2594,10 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} + p-map@7.0.4: + resolution: {integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==} + engines: {node: '>=18'} + p-queue@9.1.0: resolution: {integrity: sha512-O/ZPaXuQV29uSLbxWBGGZO1mCQXV2BLIwUr59JUU9SoH76mnYvtms7aafH/isNSNGwuEfP6W/4xD0/TJXxrizw==} engines: {node: '>=20'} @@ -2563,6 +2682,14 @@ packages: resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} engines: {node: '>=6'} + proc-log@5.0.0: + resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + promise-retry@2.0.1: + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} + property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} @@ -2652,11 +2779,18 @@ packages: retext@9.0.0: resolution: {integrity: sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==} + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + rollup@4.59.0: resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + sax@1.5.0: resolution: {integrity: sha512-21IYA3Q5cQf089Z6tgaUTr7lDAyzoTPx5HRtbhsME8Udispad8dC/+sziTNugOEx54ilvatQ9YCzl4KQLPcRHA==} engines: {node: '>=11.0.0'} @@ -2699,10 +2833,22 @@ packages: sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + smol-toml@1.6.0: resolution: {integrity: sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==} engines: {node: '>= 18'} + socks-proxy-agent@8.0.5: + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} + engines: {node: '>= 14'} + + socks@2.8.9: + resolution: {integrity: sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -2710,6 +2856,10 @@ packages: space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + ssri@12.0.0: + resolution: {integrity: sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==} + engines: {node: ^18.17.0 || >=20.5.0} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -2764,6 +2914,10 @@ packages: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} + tar@7.5.16: + resolution: {integrity: sha512-56adEpPMouktRlBLXiaYFFzZ/3+JXa8P9n7WbR+ibIjtviN55mEaOkiysCnPnWm+7kkui1Dn8J9l+g6zV8731w==} + engines: {node: '>=18'} + test-exclude@7.0.2: resolution: {integrity: sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==} engines: {node: '>=18'} @@ -2889,6 +3043,14 @@ packages: tree-sitter: optional: true + tree-sitter-systemverilog@0.3.1: + resolution: {integrity: sha512-1YjtQJhGHm3LyLO5W31FkAwgBnyO1QvIrb8qGQYIIKwGCO3oqBuq/SJOnY4GA6DgSkJK2etKVYZ1i+Rz22OYbA==} + peerDependencies: + tree-sitter: ^0.25.0 + peerDependenciesMeta: + tree-sitter: + optional: true + tree-sitter-typescript@0.23.2: resolution: {integrity: sha512-e04JUUKxTT53/x3Uq1zIL45DoYKVfHH4CZqwgZhPg5qYROl5nQjV+85ruFzFGZxu+QeFVbRTPDRnqL9UbU4VeA==} peerDependencies: @@ -2949,6 +3111,14 @@ packages: unifont@0.7.4: resolution: {integrity: sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg==} + unique-filename@4.0.0: + resolution: {integrity: sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + unique-slug@5.0.0: + resolution: {integrity: sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==} + engines: {node: ^18.17.0 || >=20.5.0} + unist-util-find-after@5.0.0: resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} @@ -3237,6 +3407,11 @@ packages: engines: {node: '>= 8'} hasBin: true + which@5.0.0: + resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} @@ -3260,6 +3435,13 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + yaml@2.8.3: resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} engines: {node: '>= 14.6'} @@ -3829,6 +4011,10 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.3 + '@istanbuljs/schema@0.1.3': {} '@jridgewell/gen-mapping@0.3.13': @@ -3850,6 +4036,20 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@npmcli/agent@3.0.0': + dependencies: + agent-base: 7.1.4 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + lru-cache: 10.4.3 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + + '@npmcli/fs@4.0.0': + dependencies: + semver: 7.7.4 + '@oslojs/encoding@1.1.0': {} '@pkgjs/parseargs@0.11.0': @@ -4344,12 +4544,16 @@ snapshots: d3-selection: 3.0.0 d3-zoom: 3.0.0 + abbrev@3.0.1: {} + acorn-jsx@5.3.2(acorn@8.16.0): dependencies: acorn: 8.16.0 acorn@8.16.0: {} + agent-base@7.1.4: {} + ajv@6.15.0: dependencies: fast-deep-equal: 3.1.3 @@ -4514,6 +4718,21 @@ snapshots: cac@6.7.14: {} + cacache@19.0.1: + dependencies: + '@npmcli/fs': 4.0.0 + fs-minipass: 3.0.3 + glob: 10.5.0 + lru-cache: 10.4.3 + minipass: 7.1.3 + minipass-collect: 2.0.1 + minipass-flush: 1.0.7 + minipass-pipeline: 1.2.4 + p-map: 7.0.4 + ssri: 12.0.0 + tar: 7.5.16 + unique-filename: 4.0.0 + callsites@3.1.0: {} caniuse-lite@1.0.30001787: {} @@ -4547,6 +4766,8 @@ snapshots: dependencies: readdirp: 5.0.0 + chownr@3.0.0: {} + ci-info@4.4.0: {} classcat@5.0.5: {} @@ -4711,6 +4932,11 @@ snapshots: emoji-regex@9.2.2: {} + encoding@0.1.13: + dependencies: + iconv-lite: 0.6.3 + optional: true + enhanced-resolve@5.20.0: dependencies: graceful-fs: 4.2.11 @@ -4720,6 +4946,10 @@ snapshots: entities@6.0.1: {} + env-paths@2.2.1: {} + + err-code@2.0.3: {} + es-module-lexer@1.7.0: {} es-module-lexer@2.0.0: {} @@ -4872,6 +5102,8 @@ snapshots: expect-type@1.3.0: {} + exponential-backoff@3.1.3: {} + extend@3.0.2: {} fast-deep-equal@3.1.3: {} @@ -4919,6 +5151,10 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 + fs-minipass@3.0.3: + dependencies: + minipass: 7.1.3 + fsevents@2.3.3: optional: true @@ -5113,6 +5349,25 @@ snapshots: http-cache-semantics@4.2.0: {} + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + optional: true + ignore@5.3.2: {} ignore@7.0.5: {} @@ -5126,6 +5381,8 @@ snapshots: inline-style-parser@0.2.7: {} + ip-address@10.2.0: {} + iron-webcrypto@1.2.1: {} is-alphabetical@2.0.1: {} @@ -5163,6 +5420,8 @@ snapshots: isexe@2.0.0: {} + isexe@3.1.5: {} + istanbul-lib-coverage@3.2.2: {} istanbul-lib-report@3.0.1: @@ -5360,6 +5619,22 @@ snapshots: dependencies: semver: 7.7.4 + make-fetch-happen@14.0.3: + dependencies: + '@npmcli/agent': 3.0.0 + cacache: 19.0.1 + http-cache-semantics: 4.2.0 + minipass: 7.1.3 + minipass-fetch: 4.0.1 + minipass-flush: 1.0.7 + minipass-pipeline: 1.2.4 + negotiator: 1.0.0 + proc-log: 5.0.0 + promise-retry: 2.0.1 + ssri: 12.0.0 + transitivePeerDependencies: + - supports-color + markdown-table@3.0.4: {} mdast-util-definitions@6.0.0: @@ -5728,8 +6003,40 @@ snapshots: dependencies: brace-expansion: 2.0.3 + minipass-collect@2.0.1: + dependencies: + minipass: 7.1.3 + + minipass-fetch@4.0.1: + dependencies: + minipass: 7.1.3 + minipass-sized: 1.0.3 + minizlib: 3.1.0 + optionalDependencies: + encoding: 0.1.13 + + minipass-flush@1.0.7: + dependencies: + minipass: 3.3.6 + + minipass-pipeline@1.2.4: + dependencies: + minipass: 3.3.6 + + minipass-sized@1.0.3: + dependencies: + minipass: 3.3.6 + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + minipass@7.1.3: {} + minizlib@3.1.0: + dependencies: + minipass: 7.1.3 + mnemonist@0.39.8: dependencies: obliterator: 2.0.5 @@ -5742,6 +6049,8 @@ snapshots: natural-compare@1.4.0: {} + negotiator@1.0.0: {} + neotraverse@0.6.18: {} nlcst-to-string@4.0.0: @@ -5754,10 +6063,29 @@ snapshots: node-gyp-build@4.8.4: {} + node-gyp@11.5.0: + dependencies: + env-paths: 2.2.1 + exponential-backoff: 3.1.3 + graceful-fs: 4.2.11 + make-fetch-happen: 14.0.3 + nopt: 8.1.0 + proc-log: 5.0.0 + semver: 7.7.4 + tar: 7.5.16 + tinyglobby: 0.2.15 + which: 5.0.0 + transitivePeerDependencies: + - supports-color + node-mock-http@1.0.4: {} node-releases@2.0.37: {} + nopt@8.1.0: + dependencies: + abbrev: 3.0.1 + normalize-path@3.0.0: {} nth-check@2.1.1: @@ -5805,6 +6133,8 @@ snapshots: dependencies: p-limit: 3.1.0 + p-map@7.0.4: {} + p-queue@9.1.0: dependencies: eventemitter3: 5.0.4 @@ -5886,6 +6216,13 @@ snapshots: prismjs@1.30.0: {} + proc-log@5.0.0: {} + + promise-retry@2.0.1: + dependencies: + err-code: 2.0.3 + retry: 0.12.0 + property-information@7.1.0: {} punycode@2.3.1: {} @@ -6026,6 +6363,8 @@ snapshots: retext-stringify: 4.0.0 unified: 11.0.5 + retry@0.12.0: {} + rollup@4.59.0: dependencies: '@types/estree': 1.0.8 @@ -6057,6 +6396,9 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.59.0 fsevents: 2.3.3 + safer-buffer@2.1.2: + optional: true + sax@1.5.0: {} scheduler@0.27.0: {} @@ -6120,12 +6462,31 @@ snapshots: sisteransi@1.0.5: {} + smart-buffer@4.2.0: {} + smol-toml@1.6.0: {} + socks-proxy-agent@8.0.5: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + socks: 2.8.9 + transitivePeerDependencies: + - supports-color + + socks@2.8.9: + dependencies: + ip-address: 10.2.0 + smart-buffer: 4.2.0 + source-map-js@1.2.1: {} space-separated-tokens@2.0.2: {} + ssri@12.0.0: + dependencies: + minipass: 7.1.3 + stackback@0.0.2: {} std-env@3.10.0: {} @@ -6187,6 +6548,14 @@ snapshots: tapable@2.3.0: {} + tar@7.5.16: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.3 + minizlib: 3.1.0 + yallist: 5.0.0 + test-exclude@7.0.2: dependencies: '@istanbuljs/schema': 0.1.3 @@ -6270,6 +6639,14 @@ snapshots: node-addon-api: 8.6.0 node-gyp-build: 4.8.4 + tree-sitter-systemverilog@0.3.1: + dependencies: + node-addon-api: 8.6.0 + node-gyp: 11.5.0 + node-gyp-build: 4.8.4 + transitivePeerDependencies: + - supports-color + tree-sitter-typescript@0.23.2: dependencies: node-addon-api: 8.6.0 @@ -6330,6 +6707,14 @@ snapshots: ofetch: 1.5.1 ohash: 2.0.11 + unique-filename@4.0.0: + dependencies: + unique-slug: 5.0.0 + + unique-slug@5.0.0: + dependencies: + imurmurhash: 0.1.4 + unist-util-find-after@5.0.0: dependencies: '@types/unist': 3.0.3 @@ -6627,6 +7012,10 @@ snapshots: dependencies: isexe: 2.0.0 + which@5.0.0: + dependencies: + isexe: 3.1.5 + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 @@ -6650,6 +7039,10 @@ snapshots: yallist@3.1.1: {} + yallist@4.0.0: {} + + yallist@5.0.0: {} + yaml@2.8.3: {} yargs-parser@22.0.0: {} diff --git a/understand-anything-plugin/packages/core/package.json b/understand-anything-plugin/packages/core/package.json index c4cd4f32..05fea29e 100644 --- a/understand-anything-plugin/packages/core/package.json +++ b/understand-anything-plugin/packages/core/package.json @@ -48,6 +48,7 @@ "tree-sitter-python": "^0.25.0", "tree-sitter-ruby": "^0.23.1", "tree-sitter-rust": "^0.24.0", + "tree-sitter-systemverilog": "^0.3.1", "tree-sitter-typescript": "^0.23.2", "web-tree-sitter": "^0.26.6", "yaml": "^2.8.3", diff --git a/understand-anything-plugin/packages/core/src/__tests__/language-registry.test.ts b/understand-anything-plugin/packages/core/src/__tests__/language-registry.test.ts index 7a3c7749..4ecad11b 100644 --- a/understand-anything-plugin/packages/core/src/__tests__/language-registry.test.ts +++ b/understand-anything-plugin/packages/core/src/__tests__/language-registry.test.ts @@ -49,10 +49,10 @@ describe("LanguageRegistry", () => { }); describe("createDefault", () => { - it("registers all 40 built-in language configs", () => { + it("registers all 41 built-in language configs", () => { const registry = LanguageRegistry.createDefault(); const all = registry.getAllLanguages(); - expect(all.length).toBe(40); + expect(all.length).toBe(41); }); it("maps all expected extensions", () => { diff --git a/understand-anything-plugin/packages/core/src/__tests__/parsers.test.ts b/understand-anything-plugin/packages/core/src/__tests__/parsers.test.ts index 10d21420..2b68fc4c 100644 --- a/understand-anything-plugin/packages/core/src/__tests__/parsers.test.ts +++ b/understand-anything-plugin/packages/core/src/__tests__/parsers.test.ts @@ -609,10 +609,11 @@ describe("EnvParser edge cases", () => { }); describe("registerAllParsers", () => { - it("registers all 12 parsers with a PluginRegistry", () => { + it("registers all 13 parsers with a PluginRegistry", () => { const registry = new PluginRegistry(); registerAllParsers(registry); - expect(registry.getPlugins()).toHaveLength(12); + expect(registry.getPlugins()).toHaveLength(13); + expect(registry.getSupportedLanguages()).toContain("systemverilog"); expect(registry.getSupportedLanguages()).toContain("markdown"); expect(registry.getSupportedLanguages()).toContain("yaml"); expect(registry.getSupportedLanguages()).toContain("json"); diff --git a/understand-anything-plugin/packages/core/src/__tests__/systemverilog-config.test.ts b/understand-anything-plugin/packages/core/src/__tests__/systemverilog-config.test.ts new file mode 100644 index 00000000..29dc7939 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/__tests__/systemverilog-config.test.ts @@ -0,0 +1,20 @@ +import { describe, it, expect } from "vitest"; +import { LanguageRegistry } from "../languages/language-registry.js"; + +describe("systemverilog language config", () => { + const reg = LanguageRegistry.createDefault(); + it.each([ + ["rtl/fifo.sv", "systemverilog"], + ["rtl/cpu.v", "systemverilog"], + ["tb/types_pkg.svh", "systemverilog"], + ["tb/defs.vh", "systemverilog"], + ])("detects %s as %s", (path, id) => { + expect(reg.getForFile(path)?.id).toBe(id); + }); + + it("exposes HDL/UVM concepts", () => { + const cfg = reg.getForFile("x.sv"); + expect(cfg?.displayName).toBe("SystemVerilog"); + expect(cfg?.concepts).toContain("uvm"); + }); +}); diff --git a/understand-anything-plugin/packages/core/src/__tests__/systemverilog-plugin.test.ts b/understand-anything-plugin/packages/core/src/__tests__/systemverilog-plugin.test.ts new file mode 100644 index 00000000..77854c09 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/__tests__/systemverilog-plugin.test.ts @@ -0,0 +1,20 @@ +import { describe, it, expect } from "vitest"; +import { PluginRegistry } from "../plugins/registry.js"; +import { registerAllParsers } from "../plugins/parsers/index.js"; + +describe("SystemVerilog AnalyzerPlugin", () => { + const reg = new PluginRegistry(); + registerAllParsers(reg); + + it("routes .sv files to the sv parser", () => { + expect(reg.getPluginForFile("rtl/alu.sv")?.name).toBe("systemverilog-parser"); + }); + it("routes .v files to the sv parser", () => { + expect(reg.getPluginForFile("rtl/cpu.v")?.name).toBe("systemverilog-parser"); + }); + it("emits module definitions and UVM classes", () => { + const a = reg.analyzeFile("tb/d.sv", "module top; endmodule\nclass d extends uvm_driver; endclass")!; + expect(a.definitions?.some((d) => d.kind === "module" && d.name === "top")).toBe(true); + expect(a.classes.some((c) => c.name === "d")).toBe(true); + }); +}); diff --git a/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/rtl/alu.sv b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/rtl/alu.sv new file mode 100644 index 00000000..cc069beb --- /dev/null +++ b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/rtl/alu.sv @@ -0,0 +1,20 @@ +// alu — minimal ALU using dut_pkg::op_e +module alu #(parameter int W = 8) ( + input logic clk, + input logic [W-1:0] a, + input logic [W-1:0] b, + output logic [W-1:0] y +); + import dut_pkg::*; + op_e op; + + always_comb begin + unique case (op) + OP_ADD: y = a + b; + OP_SUB: y = a - b; + OP_AND: y = a & b; + OP_OR: y = a | b; + default: y = '0; + endcase + end +endmodule diff --git a/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/rtl/arbiter.sv b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/rtl/arbiter.sv new file mode 100644 index 00000000..84ddc57c --- /dev/null +++ b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/rtl/arbiter.sv @@ -0,0 +1,17 @@ +// arbiter — fixed-priority N-request arbiter +module arbiter #(parameter int N = 4) ( + input logic clk, + input logic rst_n, + input logic [N-1:0] reqs, + output logic [N-1:0] grants +); + always_comb begin + grants = '0; + for (int i = 0; i < N; i++) begin + if (reqs[i]) begin + grants[i] = 1'b1; + break; + end + end + end +endmodule diff --git a/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/rtl/dut_pkg.sv b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/rtl/dut_pkg.sv new file mode 100644 index 00000000..6e9de630 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/rtl/dut_pkg.sv @@ -0,0 +1,9 @@ +// dut_pkg — shared types for the demo DUT +package dut_pkg; + typedef enum logic [1:0] { OP_ADD, OP_SUB, OP_AND, OP_OR } op_e; + typedef struct packed { + logic [7:0] addr; + logic [7:0] data; + } bus_t; + parameter int BUS_WIDTH = 8; +endpackage diff --git a/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/rtl/fifo.sv b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/rtl/fifo.sv new file mode 100644 index 00000000..6a18b866 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/rtl/fifo.sv @@ -0,0 +1,22 @@ +// fifo — simple synchronous FIFO +module fifo #(parameter int W = 8, parameter int DEPTH = 16) ( + input logic clk, + input logic rst_n, + input logic push, + input logic pop, + input logic [W-1:0] din, + output logic [W-1:0] dout, + output logic full, + output logic empty +); + logic [W-1:0] mem [0:DEPTH-1]; + logic [$clog2(DEPTH):0] count; + + assign full = (count == DEPTH); + assign empty = (count == 0); + + always_ff @(posedge clk) begin + if (!rst_n) count <= '0; + else count <= count + (push & ~full) - (pop & ~empty); + end +endmodule diff --git a/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/rtl/top.sv b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/rtl/top.sv new file mode 100644 index 00000000..e0288317 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/rtl/top.sv @@ -0,0 +1,18 @@ +// top — design top that instantiates the RTL submodules +module top #(parameter int W = 8) ( + input logic clk, + input logic rst_n +); + import dut_pkg::*; + + logic [W-1:0] a, b, y, din, dout; + logic [3:0] reqs, grants; + logic push, pop, full, empty; + + alu #(.W(W)) u_alu (.clk(clk), .a(a), .b(b), .y(y)); + fifo #(.W(W), .DEPTH(16)) u_fifo (.clk(clk), .rst_n(rst_n), .push(push), + .pop(pop), .din(din), .dout(dout), + .full(full), .empty(empty)); + arbiter #(.N(4)) u_arbiter (.clk(clk), .rst_n(rst_n), + .reqs(reqs), .grants(grants)); +endmodule diff --git a/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_agent.sv b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_agent.sv new file mode 100644 index 00000000..ce7b8c41 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_agent.sv @@ -0,0 +1,23 @@ +// dut_agent — wraps driver + monitor + sequencer +class dut_agent extends uvm_agent; + `uvm_component_utils(dut_agent) + + dut_driver drv; + dut_monitor mon; + dut_sequencer sqr; + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + drv = dut_driver::type_id::create("drv", this); + mon = dut_monitor::type_id::create("mon", this); + sqr = dut_sequencer::type_id::create("sqr", this); + endfunction + + function void connect_phase(uvm_phase phase); + drv.seq_item_port.connect(sqr.seq_item_export); + endfunction +endclass diff --git a/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_base_test.sv b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_base_test.sv new file mode 100644 index 00000000..56f26535 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_base_test.sv @@ -0,0 +1,23 @@ +// dut_base_test — base UVM test: builds the env and runs the sequence +class dut_base_test extends uvm_test; + `uvm_component_utils(dut_base_test) + + dut_env env; + + function new(string name = "dut_base_test", uvm_component parent = null); + super.new(name, parent); + endfunction + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + env = dut_env::type_id::create("env", this); + endfunction + + task run_phase(uvm_phase phase); + dut_sequence seq; + phase.raise_objection(this); + seq = dut_sequence::type_id::create("seq"); + seq.start(env.agt.sqr); + phase.drop_objection(this); + endtask +endclass diff --git a/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_driver.sv b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_driver.sv new file mode 100644 index 00000000..a0b6e112 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_driver.sv @@ -0,0 +1,18 @@ +// dut_driver — drives transactions onto the DUT interface +class dut_driver extends uvm_driver #(dut_seq_item); + `uvm_component_utils(dut_driver) + + virtual dut_if vif; + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction + + task run_phase(uvm_phase phase); + forever begin + seq_item_port.get_next_item(req); + // drive req.data onto vif.cb ... + seq_item_port.item_done(); + end + endtask +endclass diff --git a/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_env.sv b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_env.sv new file mode 100644 index 00000000..027e5d5a --- /dev/null +++ b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_env.sv @@ -0,0 +1,21 @@ +// dut_env — top-level UVM environment (agent + scoreboard) +class dut_env extends uvm_env; + `uvm_component_utils(dut_env) + + dut_agent agt; + dut_scoreboard sb; + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + agt = dut_agent::type_id::create("agt", this); + sb = dut_scoreboard::type_id::create("sb", this); + endfunction + + function void connect_phase(uvm_phase phase); + agt.mon.ap.connect(sb.imp); + endfunction +endclass diff --git a/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_if.sv b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_if.sv new file mode 100644 index 00000000..5b611d0e --- /dev/null +++ b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_if.sv @@ -0,0 +1,12 @@ +// dut_if — DUT interface with a clocking block +interface dut_if (input logic clk); + logic rst_n; + logic [7:0] data; + logic valid; + logic ready; + + clocking cb @(posedge clk); + output data, valid; + input ready; + endclocking +endinterface diff --git a/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_monitor.sv b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_monitor.sv new file mode 100644 index 00000000..8dddee1d --- /dev/null +++ b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_monitor.sv @@ -0,0 +1,16 @@ +// dut_monitor — samples the interface and publishes observed items +class dut_monitor extends uvm_monitor; + `uvm_component_utils(dut_monitor) + + virtual dut_if vif; + uvm_analysis_port #(dut_seq_item) ap; + + function new(string name, uvm_component parent); + super.new(name, parent); + ap = new("ap", this); + endfunction + + task run_phase(uvm_phase phase); + // sample vif.cb, build dut_seq_item, ap.write(item) ... + endtask +endclass diff --git a/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_scoreboard.sv b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_scoreboard.sv new file mode 100644 index 00000000..e4b750a5 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_scoreboard.sv @@ -0,0 +1,15 @@ +// dut_scoreboard — checks observed transactions against a reference model +class dut_scoreboard extends uvm_scoreboard; + `uvm_component_utils(dut_scoreboard) + + uvm_analysis_imp #(dut_seq_item, dut_scoreboard) imp; + + function new(string name, uvm_component parent); + super.new(name, parent); + imp = new("imp", this); + endfunction + + function void write(dut_seq_item item); + // compare item against the reference model ... + endfunction +endclass diff --git a/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_seq_item.sv b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_seq_item.sv new file mode 100644 index 00000000..5d73eee2 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_seq_item.sv @@ -0,0 +1,14 @@ +// dut_seq_item — UVM transaction (sequence item) +class dut_seq_item extends uvm_sequence_item; + rand bit [7:0] addr; + rand bit [7:0] data; + + `uvm_object_utils_begin(dut_seq_item) + `uvm_field_int(addr, UVM_ALL_ON) + `uvm_field_int(data, UVM_ALL_ON) + `uvm_object_utils_end + + function new(string name = "dut_seq_item"); + super.new(name); + endfunction +endclass diff --git a/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_sequence.sv b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_sequence.sv new file mode 100644 index 00000000..9141853c --- /dev/null +++ b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_sequence.sv @@ -0,0 +1,18 @@ +// dut_sequence — produces a stream of transactions +class dut_sequence extends uvm_sequence #(dut_seq_item); + `uvm_object_utils(dut_sequence) + + function new(string name = "dut_sequence"); + super.new(name); + endfunction + + task body(); + dut_seq_item req; + repeat (8) begin + req = dut_seq_item::type_id::create("req"); + start_item(req); + assert (req.randomize()); + finish_item(req); + end + endtask +endclass diff --git a/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_sequencer.sv b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_sequencer.sv new file mode 100644 index 00000000..03f8373c --- /dev/null +++ b/understand-anything-plugin/packages/core/src/analyzer/__tests__/fixtures/uvm_demo/tb/dut_sequencer.sv @@ -0,0 +1,8 @@ +// dut_sequencer — arbitrates sequences onto the driver +class dut_sequencer extends uvm_sequencer #(dut_seq_item); + `uvm_component_utils(dut_sequencer) + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction +endclass diff --git a/understand-anything-plugin/packages/core/src/analyzer/__tests__/hdl-corpus-treesitter.integration.test.ts b/understand-anything-plugin/packages/core/src/analyzer/__tests__/hdl-corpus-treesitter.integration.test.ts new file mode 100644 index 00000000..02120a21 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/analyzer/__tests__/hdl-corpus-treesitter.integration.test.ts @@ -0,0 +1,83 @@ +// Same corpus contract as hdl-corpus.integration.test.ts, but built through the +// tree-sitter backend — proving both parsers yield an equivalent knowledge graph. +import { describe, it, expect, beforeAll } from "vitest"; +import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; +import { existsSync, readFileSync, readdirSync } from "node:fs"; +import { buildHdlGraphTreeSitter, type HdlFile } from "../hdl-graph.js"; +import type { KnowledgeGraph } from "../../types.js"; +import { validateGraph } from "../../schema.js"; + +function findCorpus(): string { + const dir = join(dirname(fileURLToPath(import.meta.url)), "fixtures", "uvm_demo"); + if (!existsSync(dir)) throw new Error("uvm_demo fixture not found at " + dir); + return dir; +} +function loadCorpus(dir: string): HdlFile[] { + return (readdirSync(dir, { recursive: true }) as string[]) + .map((p) => p.replace(/\\/g, "/")) + .filter((p) => /\.(sv|svh|v|vh)$/.test(p)) + .map((p) => ({ path: p, content: readFileSync(join(dir, p), "utf8") })); +} + +describe("uvm_demo corpus — tree-sitter backend", () => { + let g: KnowledgeGraph; + beforeAll(async () => { + g = await buildHdlGraphTreeSitter(loadCorpus(findCorpus()), "uvm_demo", "test"); + }); + const nameOf = (id: string): string => g.nodes.find((n) => n.id === id)!.name; + const composes = (parent: string): string[] => + g.edges.filter((e) => e.source === `class:${parent}` && e.type === "depends_on").map((e) => nameOf(e.target)).sort(); + + it("is schema-valid", () => { + const res = validateGraph(g); + if (!res.success) console.error("issues:", JSON.stringify(res.issues), res.fatal); + expect(res.success).toBe(true); + }); + + it("extracts the 4 RTL modules", () => { + const mods = g.nodes.filter((n) => n.type === "module").map((n) => n.name); + expect(mods).toEqual(expect.arrayContaining(["alu", "arbiter", "fifo", "top"])); + }); + + it("top depends_on its three submodules", () => { + const deps = g.edges.filter((e) => e.source === "module:top" && e.type === "depends_on").map((e) => nameOf(e.target)).sort(); + expect(deps).toEqual(["alu", "arbiter", "fifo"]); + }); + + it("has 9 UVM inheritance edges", () => { + expect(g.edges.filter((e) => e.type === "inherits").length).toBe(9); + }); + + it("extracts the UVM composition tree from type_id::create", () => { + expect(composes("dut_base_test")).toEqual(["dut_env", "dut_sequence"]); + expect(composes("dut_env")).toEqual(["dut_agent", "dut_scoreboard"]); + expect(composes("dut_agent")).toEqual(["dut_driver", "dut_monitor", "dut_sequencer"]); + expect(composes("dut_sequence")).toEqual(["dut_seq_item"]); + }); + + it("resolves TLM connect() into publishes edges", () => { + const tlm = g.edges.filter((e) => e.type === "publishes").map((e) => `${nameOf(e.source)}->${nameOf(e.target)}`).sort(); + expect(tlm).toEqual(["dut_driver->dut_sequencer", "dut_monitor->dut_scoreboard"]); + }); + + it("converges: the whole testbench is reachable from dut_base_test via composition", () => { + const adj = new Map(); + for (const e of g.edges.filter((e) => e.type === "depends_on")) { + const list = adj.get(e.source) ?? adj.set(e.source, []).get(e.source)!; + list.push(e.target); + } + const seen = new Set(); + const stack = ["class:dut_base_test"]; + while (stack.length) { + const cur = stack.pop()!; + if (seen.has(cur)) continue; + seen.add(cur); + for (const next of adj.get(cur) ?? []) stack.push(next); + } + for (const c of ["dut_env", "dut_sequence", "dut_agent", "dut_scoreboard", + "dut_driver", "dut_monitor", "dut_sequencer", "dut_seq_item"]) { + expect(seen.has(`class:${c}`), `${c} reachable`).toBe(true); + } + }); +}); diff --git a/understand-anything-plugin/packages/core/src/analyzer/__tests__/hdl-corpus.integration.test.ts b/understand-anything-plugin/packages/core/src/analyzer/__tests__/hdl-corpus.integration.test.ts new file mode 100644 index 00000000..2b81217d --- /dev/null +++ b/understand-anything-plugin/packages/core/src/analyzer/__tests__/hdl-corpus.integration.test.ts @@ -0,0 +1,111 @@ +import { describe, it, expect } from "vitest"; +import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; +import { existsSync, readFileSync, readdirSync } from "node:fs"; +import { buildHdlGraph, type HdlFile } from "../hdl-graph.js"; +import { validateGraph } from "../../schema.js"; + +/** The UVM demo corpus is vendored beside this test so the suite is self-contained on CI. */ +function findCorpus(): string { + const dir = join(dirname(fileURLToPath(import.meta.url)), "fixtures", "uvm_demo"); + if (!existsSync(dir)) throw new Error("uvm_demo fixture not found at " + dir); + return dir; +} + +function loadCorpus(dir: string): HdlFile[] { + return (readdirSync(dir, { recursive: true }) as string[]) + .map((p) => p.replace(/\\/g, "/")) + .filter((p) => /\.(sv|svh|v|vh)$/.test(p)) + .map((p) => ({ path: p, content: readFileSync(join(dir, p), "utf8") })); +} + +describe("uvm_demo corpus", () => { + const files = loadCorpus(findCorpus()); + const g = buildHdlGraph(files, "uvm_demo", "test"); + const nameOf = (id: string) => g.nodes.find((n) => n.id === id)!.name; + + it("loaded all 15 corpus files", () => expect(files.length).toBe(15)); + + it("is schema-valid", () => { + const res = validateGraph(g); + if (!res.success) console.error("issues:", JSON.stringify(res.issues), res.fatal); + expect(res.success).toBe(true); + }); + + it("module nodes include the 4 RTL modules", () => { + const mods = g.nodes.filter((n) => n.type === "module").map((n) => n.name); + expect(mods).toEqual(expect.arrayContaining(["alu", "arbiter", "fifo", "top"])); + }); + + it("top depends_on the three submodules", () => { + const top = g.nodes.find((n) => n.type === "module" && n.name === "top")!; + const deps = g.edges + .filter((e) => e.source === top.id && e.type === "depends_on") + .map((e) => nameOf(e.target)) + .sort(); + expect(deps).toEqual(["alu", "arbiter", "fifo"]); + }); + + it("has 9 UVM inheritance edges to the expected bases", () => { + const inh = g.edges.filter((e) => e.type === "inherits"); + expect(inh.length).toBe(9); + const bases = inh.map((e) => nameOf(e.target)).sort(); + expect(bases).toEqual( + [ + "uvm_agent", "uvm_driver", "uvm_env", "uvm_monitor", "uvm_scoreboard", + "uvm_sequence", "uvm_sequence_item", "uvm_sequencer", "uvm_test", + ].sort(), + ); + }); + + it("dut_driver inherits uvm_driver", () => { + const drv = g.nodes.find((n) => n.type === "class" && n.name === "dut_driver")!; + const base = g.edges.find((e) => e.source === drv.id && e.type === "inherits")!; + expect(nameOf(base.target)).toBe("uvm_driver"); + }); + + it("creates external uvm_* base class nodes", () => + expect(g.nodes.some((n) => n.type === "class" && n.name === "uvm_sequence_item")).toBe(true)); + + const composes = (parent: string): string[] => + g.edges + .filter((e) => e.source === `class:${parent}` && e.type === "depends_on") + .map((e) => nameOf(e.target)) + .sort(); + + it("extracts the UVM composition tree from type_id::create", () => { + expect(composes("dut_base_test")).toEqual(["dut_env", "dut_sequence"]); + expect(composes("dut_env")).toEqual(["dut_agent", "dut_scoreboard"]); + expect(composes("dut_agent")).toEqual(["dut_driver", "dut_monitor", "dut_sequencer"]); + expect(composes("dut_sequence")).toEqual(["dut_seq_item"]); + }); + + it("resolves TLM connect() into publishes edges across the env", () => { + const tlm = g.edges + .filter((e) => e.type === "publishes") + .map((e) => `${nameOf(e.source)}->${nameOf(e.target)}`) + .sort(); + expect(tlm).toEqual(["dut_driver->dut_sequencer", "dut_monitor->dut_scoreboard"]); + }); + + it("converges: the whole testbench is reachable from dut_base_test via composition", () => { + const adj = new Map(); + for (const e of g.edges.filter((e) => e.type === "depends_on")) { + const list = adj.get(e.source) ?? adj.set(e.source, []).get(e.source)!; + list.push(e.target); + } + const seen = new Set(); + const stack = ["class:dut_base_test"]; + while (stack.length) { + const cur = stack.pop()!; + if (seen.has(cur)) continue; + seen.add(cur); + for (const next of adj.get(cur) ?? []) stack.push(next); + } + const tbComponents = ["dut_env", "dut_sequence", "dut_agent", "dut_scoreboard", + "dut_driver", "dut_monitor", "dut_sequencer", "dut_seq_item"]; + for (const c of tbComponents) { + expect(seen.has(`class:${c}`), `${c} reachable from dut_base_test`).toBe(true); + } + }); +}); diff --git a/understand-anything-plugin/packages/core/src/analyzer/__tests__/hdl-graph.test.ts b/understand-anything-plugin/packages/core/src/analyzer/__tests__/hdl-graph.test.ts new file mode 100644 index 00000000..07f83869 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/analyzer/__tests__/hdl-graph.test.ts @@ -0,0 +1,149 @@ +import { describe, it, expect } from "vitest"; +import { buildHdlGraph } from "../hdl-graph.js"; +import { validateGraph } from "../../schema.js"; + +const files = [ + { path: "rtl/alu.sv", content: "module alu; endmodule" }, + { path: "rtl/fifo.sv", content: "module fifo; endmodule" }, + { path: "rtl/top.sv", content: "module top; alu u_alu(); fifo u_fifo(); endmodule" }, + { path: "tb/item.sv", content: "class my_item extends uvm_sequence_item; endclass" }, + { path: "tb/drv.sv", content: "class my_driver extends uvm_driver #(my_item); endclass" }, +]; + +describe("buildHdlGraph", () => { + const g = buildHdlGraph(files, "uvm_demo", "deadbeef"); + + it("produces a schema-valid graph", () => { + const res = validateGraph(g); + if (!res.success) console.error("validation issues:", JSON.stringify(res.issues), res.fatal); + expect(res.success).toBe(true); + }); + + it("creates module nodes", () => + expect(g.nodes.filter((n) => n.type === "module").map((n) => n.name).sort()) + .toEqual(["alu", "fifo", "top"])); + + it("creates depends_on edges for instantiations", () => { + const top = g.nodes.find((n) => n.type === "module" && n.name === "top")!; + const deps = g.edges.filter((e) => e.source === top.id && e.type === "depends_on") + .map((e) => g.nodes.find((n) => n.id === e.target)!.name).sort(); + expect(deps).toEqual(["alu", "fifo"]); + }); + + it("creates inherits edges up to uvm_* bases (external nodes allowed)", () => { + const drv = g.nodes.find((n) => n.type === "class" && n.name === "my_driver")!; + const base = g.edges.find((e) => e.source === drv.id && e.type === "inherits"); + expect(base).toBeTruthy(); + expect(g.nodes.find((n) => n.id === base!.target)!.name).toBe("uvm_driver"); + }); + + it("creates external uvm_* base class nodes", () => + expect(g.nodes.some((n) => n.type === "class" && n.name === "uvm_sequence_item")).toBe(true)); + + it("emits RTL + UVM layers so the dashboard can render them", () => { + expect(g.layers.map((l) => l.name)).toEqual(["RTL Design", "UVM Testbench"]); + const rtl = g.layers.find((l) => l.id === "layer:rtl")!; + expect(rtl.nodeIds).toContain("module:top"); + }); +}); + +// A minimal-but-complete UVM testbench: env owns agent+scoreboard, agent owns +// driver+monitor+sequencer, test owns env+sequence, sequence makes seq_items. +const uvmFiles = [ + { path: "tb/item.sv", content: "class dut_seq_item extends uvm_sequence_item; endclass" }, + { path: "tb/driver.sv", content: "class dut_driver extends uvm_driver #(dut_seq_item); endclass" }, + { path: "tb/sequencer.sv", content: "class dut_sequencer extends uvm_sequencer #(dut_seq_item); endclass" }, + { path: "tb/monitor.sv", content: "class dut_monitor extends uvm_monitor; endclass" }, + { path: "tb/scoreboard.sv", content: "class dut_scoreboard extends uvm_scoreboard; endclass" }, + { + path: "tb/sequence.sv", + content: `class dut_sequence extends uvm_sequence #(dut_seq_item); + task body(); + dut_seq_item req; + req = dut_seq_item::type_id::create("req"); + endtask + endclass`, + }, + { + path: "tb/agent.sv", + content: `class dut_agent extends uvm_agent; + dut_driver drv; dut_monitor mon; dut_sequencer sqr; + function void build_phase(uvm_phase phase); + drv = dut_driver::type_id::create("drv", this); + mon = dut_monitor::type_id::create("mon", this); + sqr = dut_sequencer::type_id::create("sqr", this); + endfunction + function void connect_phase(uvm_phase phase); + drv.seq_item_port.connect(sqr.seq_item_export); + endfunction + endclass`, + }, + { + path: "tb/env.sv", + content: `class dut_env extends uvm_env; + dut_agent agt; dut_scoreboard sb; + function void build_phase(uvm_phase phase); + agt = dut_agent::type_id::create("agt", this); + sb = dut_scoreboard::type_id::create("sb", this); + endfunction + function void connect_phase(uvm_phase phase); + agt.mon.ap.connect(sb.imp); + endfunction + endclass`, + }, + { + path: "tb/test.sv", + content: `class dut_base_test extends uvm_test; + dut_env env; + function void build_phase(uvm_phase phase); + env = dut_env::type_id::create("env", this); + endfunction + task run_phase(uvm_phase phase); + dut_sequence seq; + seq = dut_sequence::type_id::create("seq"); + endtask + endclass`, + }, +]; + +describe("buildHdlGraph — UVM composition + TLM", () => { + const g = buildHdlGraph(uvmFiles, "uvm_tb", "deadbeef"); + const id = (name: string): string => `class:${name}`; + const childrenVia = (parent: string, type: string): string[] => + g.edges + .filter((e) => e.source === id(parent) && e.type === type) + .map((e) => g.nodes.find((n) => n.id === e.target)!.name) + .sort(); + + it("emits composition (depends_on) edges from each component to what it creates", () => { + expect(childrenVia("dut_base_test", "depends_on")).toEqual(["dut_env", "dut_sequence"]); + expect(childrenVia("dut_env", "depends_on")).toEqual(["dut_agent", "dut_scoreboard"]); + expect(childrenVia("dut_agent", "depends_on")).toEqual(["dut_driver", "dut_monitor", "dut_sequencer"]); + expect(childrenVia("dut_sequence", "depends_on")).toEqual(["dut_seq_item"]); + }); + + it("resolves TLM connect() chains to publishes edges between components", () => { + // agt.mon.ap.connect(sb.imp) -> monitor publishes to scoreboard + expect(childrenVia("dut_monitor", "publishes")).toEqual(["dut_scoreboard"]); + // drv.seq_item_port.connect(sqr.seq_item_export) -> driver <-> sequencer data path + expect(childrenVia("dut_driver", "publishes")).toEqual(["dut_sequencer"]); + }); + + it("converges: every TB component is reachable from dut_base_test via composition", () => { + const adj = new Map(); + for (const e of g.edges.filter((e) => e.type === "depends_on")) { + (adj.get(e.source) ?? adj.set(e.source, []).get(e.source)!).push(e.target); + } + const seen = new Set(); + const stack = [id("dut_base_test")]; + while (stack.length) { + const cur = stack.pop()!; + if (seen.has(cur)) continue; + seen.add(cur); + for (const next of adj.get(cur) ?? []) stack.push(next); + } + const components = ["dut_env", "dut_sequence", "dut_agent", "dut_scoreboard", + "dut_driver", "dut_monitor", "dut_sequencer", "dut_seq_item"]; + for (const c of components) expect(seen.has(id(c))).toBe(true); + }); +}); diff --git a/understand-anything-plugin/packages/core/src/analyzer/hdl-graph.ts b/understand-anything-plugin/packages/core/src/analyzer/hdl-graph.ts new file mode 100644 index 00000000..357846e2 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/analyzer/hdl-graph.ts @@ -0,0 +1,163 @@ +// Deterministic HDL graph assembler. +// +// UA's deterministic GraphBuilder only emits file/function/class + contains edges; +// the rich design edges (instantiation, inheritance, UVM composition, TLM) normally +// come from the LLM agent pipeline. This assembler produces those edges WITHOUT an +// LLM run, so an SV/UVM project can be rendered + asserted deterministically: +// - module/class/function/file nodes +// - contains (file -> symbol) +// - depends_on (module -> instantiated module; class -> factory-created component) +// - inherits (class -> base class; external uvm_* bases get lightweight nodes) +// - publishes (TLM connect() data path between resolved components) +// +// Parsing is separated from assembly: `assembleHdlGraph` is parser-agnostic and +// produces identical output whether the facts came from the regex backend +// (`buildHdlGraph`) or the tree-sitter backend (`buildHdlGraphTreeSitter`). + +import type { KnowledgeGraph, GraphNode, GraphEdge } from "../types.js"; +import { analyzeHdl, type HdlAnalysis } from "../plugins/parsers/systemverilog-parser.js"; + +export interface HdlFile { path: string; content: string; } +export interface ParsedHdlFile { path: string; h: HdlAnalysis; } + +const moduleId = (name: string): string => `module:${name}`; +const classId = (name: string): string => `class:${name}`; +const fileId = (p: string): string => `file:${p}`; + +/** Parse SV/Verilog with the regex backend, then assemble the graph. */ +export function buildHdlGraph(files: HdlFile[], projectName: string, gitHash: string): KnowledgeGraph { + return assembleHdlGraph(files.map((f) => ({ path: f.path, h: analyzeHdl(f.content) })), projectName, gitHash); +} + +/** Parse with the tree-sitter backend (accurate AST), then assemble the same graph. + * Async because web-tree-sitter loads its WASM grammar lazily. */ +export async function buildHdlGraphTreeSitter(files: HdlFile[], projectName: string, gitHash: string): Promise { + const { analyzeHdlTreeSitter } = await import("../plugins/parsers/systemverilog-treesitter.js"); + const parsed: ParsedHdlFile[] = []; + for (const f of files) parsed.push({ path: f.path, h: await analyzeHdlTreeSitter(f.content) }); + return assembleHdlGraph(parsed, projectName, gitHash); +} + +/** Assemble the knowledge graph from already-parsed HDL facts (parser-agnostic). */ +export function assembleHdlGraph(parsed: ParsedHdlFile[], projectName: string, gitHash: string): KnowledgeGraph { + const nodes: GraphNode[] = []; + const edges: GraphEdge[] = []; + const nodeIds = new Set(); + const add = (n: GraphNode): void => { + if (!nodeIds.has(n.id)) { nodeIds.add(n.id); nodes.push(n); } + }; + const edge = (source: string, target: string, type: GraphEdge["type"], weight = 1): void => { + if (nodeIds.has(source) && nodeIds.has(target)) { + edges.push({ source, target, type, direction: "forward", weight }); + } + }; + + const moduleNames = new Set(parsed.flatMap((p) => p.h.modules.map((m) => m.name))); + const classNames = new Set(parsed.flatMap((p) => p.h.classes.map((c) => c.name))); + + // Member handle -> component type, harvested from each class's factory creates + // (`agt = dut_agent::type_id::create(...)`). Used to resolve TLM connect() chains. + const memberType = new Map>(); + for (const { h } of parsed) { + for (const c of h.classes) { + let mm = memberType.get(c.name); + if (!mm) { mm = new Map(); memberType.set(c.name, mm); } + for (const cr of c.creates) if (cr.handle) mm.set(cr.handle, cr.type); + } + } + // Walk a dotted handle chain (`agt.mon.ap`) from its owning class to the deepest + // component it resolves to (dut_monitor); non-component tokens (ports) end the walk. + const resolveChain = (ownerClass: string, chain: string): string | null => { + let current = ownerClass; + let resolved: string | null = null; + for (const tok of chain.split(".")) { + const t = memberType.get(current)?.get(tok); + if (t && classNames.has(t)) { resolved = t; current = t; } + else break; + } + return resolved; + }; + + // Pass 1: file + module + class + function nodes (with contains edges) + for (const { path, h } of parsed) { + add({ id: fileId(path), type: "file", name: path.split("/").pop() ?? path, filePath: path, summary: `HDL source file ${path}`, tags: ["hdl"], complexity: "simple" }); + for (const m of h.modules) { + add({ id: moduleId(m.name), type: "module", name: m.name, filePath: path, lineRange: m.lineRange, + summary: `${m.kind} ${m.name} (${m.ports.length} ports, ${m.params.length} params)`, tags: [m.kind], complexity: "moderate" }); + edge(fileId(path), moduleId(m.name), "contains"); + } + for (const c of h.classes) { + add({ id: classId(c.name), type: "class", name: c.name, filePath: path, lineRange: c.lineRange, + summary: c.base ? `class ${c.name} extends ${c.base}` : `class ${c.name}`, tags: ["uvm"], complexity: "moderate" }); + edge(fileId(path), classId(c.name), "contains"); + } + for (const fn of h.functions) { + const id = `function:${path}:${fn.name}`; + add({ id, type: "function", name: fn.name, filePath: path, lineRange: fn.lineRange, summary: `${fn.kind} ${fn.name}`, tags: [fn.kind], complexity: "simple" }); + edge(fileId(path), id, "contains"); + } + } + + // Pass 2: instantiation (depends_on) + inheritance (inherits) edges + for (const { h } of parsed) { + for (const m of h.modules) { + for (const inst of m.instantiations) { + if (moduleNames.has(inst.type)) edge(moduleId(m.name), moduleId(inst.type), "depends_on", 0.9); + } + } + for (const c of h.classes) { + if (!c.base) continue; + if (!classNames.has(c.base)) { + add({ id: classId(c.base), type: "class", name: c.base, summary: `external base class ${c.base}`, + tags: ["external", c.base.startsWith("uvm_") ? "uvm" : "lib"], complexity: "simple" }); + } + edge(classId(c.name), classId(c.base), "inherits", 0.9); + } + // Composition ("has-a"): the convergent UVM component tree, rooted at the test. + // `handle = Created::type_id::create(...)` -> depends_on edge to the created class. + for (const c of h.classes) { + const composed = new Set(); + for (const cr of c.creates) { + if (classNames.has(cr.type) && !composed.has(cr.type)) { + composed.add(cr.type); + edge(classId(c.name), classId(cr.type), "depends_on", 0.9); + } + } + // TLM data path: `a.b.connect(c.d)` -> publishes edge between resolved components. + for (const cn of c.connects) { + const from = resolveChain(c.name, cn.from); + const to = resolveChain(c.name, cn.to); + if (from && to && from !== to) edge(classId(from), classId(to), "publishes", 0.6); + } + } + } + + // Group nodes into layers so the dashboard's layer-driven structural view renders them. + // (UA's GraphView requires an active layer; LLM-built graphs always carry layers, ours must too.) + const rtlIds: string[] = []; + const tbIds: string[] = []; + for (const n of nodes) { + if (n.filePath && n.filePath.startsWith("rtl/")) rtlIds.push(n.id); + else tbIds.push(n.id); + } + const layers = [ + { id: "layer:rtl", name: "RTL Design", description: "Synthesizable RTL modules and their instantiation hierarchy.", nodeIds: rtlIds }, + { id: "layer:uvm", name: "UVM Testbench", description: "UVM verification environment and its class hierarchy.", nodeIds: tbIds }, + ]; + + return { + version: "1.0.0", + project: { + name: projectName, + languages: ["systemverilog"], + frameworks: ["uvm"], + description: "HDL structural graph (deterministic)", + analyzedAt: new Date().toISOString(), + gitCommitHash: gitHash, + }, + nodes, + edges, + layers, + tour: [], + }; +} diff --git a/understand-anything-plugin/packages/core/src/index.ts b/understand-anything-plugin/packages/core/src/index.ts index c5f629aa..01691801 100644 --- a/understand-anything-plugin/packages/core/src/index.ts +++ b/understand-anything-plugin/packages/core/src/index.ts @@ -114,8 +114,23 @@ export { TerraformParser, MakefileParser, ShellParser, + SystemVerilogParser, registerAllParsers, } from "./plugins/parsers/index.js"; +export { + analyzeHdl, + type HdlAnalysis, + type HdlModule, + type HdlClass, + type HdlFunction, +} from "./plugins/parsers/systemverilog-parser.js"; +export { + buildHdlGraph, + buildHdlGraphTreeSitter, + assembleHdlGraph, + type HdlFile, + type ParsedHdlFile, +} from "./analyzer/hdl-graph.js"; export { createIgnoreFilter, DEFAULT_IGNORE_PATTERNS, diff --git a/understand-anything-plugin/packages/core/src/languages/configs/index.ts b/understand-anything-plugin/packages/core/src/languages/configs/index.ts index 4893c4d5..a8e9a46f 100644 --- a/understand-anything-plugin/packages/core/src/languages/configs/index.ts +++ b/understand-anything-plugin/packages/core/src/languages/configs/index.ts @@ -13,6 +13,7 @@ import { cConfig } from "./c.js"; import { cppConfig } from "./cpp.js"; import { csharpConfig } from "./csharp.js"; import { luaConfig } from "./lua.js"; +import { systemverilogConfig } from "./systemverilog.js"; // Non-code language configs import { markdownConfig } from "./markdown.js"; import { yamlConfig } from "./yaml.js"; @@ -57,6 +58,7 @@ export const builtinLanguageConfigs: LanguageConfig[] = [ cConfig, cppConfig, csharpConfig, + systemverilogConfig, // Non-code languages markdownConfig, yamlConfig, @@ -102,6 +104,7 @@ export { cConfig, cppConfig, csharpConfig, + systemverilogConfig, // Non-code languages markdownConfig, yamlConfig, diff --git a/understand-anything-plugin/packages/core/src/languages/configs/systemverilog.ts b/understand-anything-plugin/packages/core/src/languages/configs/systemverilog.ts new file mode 100644 index 00000000..c9674fb5 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/languages/configs/systemverilog.ts @@ -0,0 +1,21 @@ +import type { LanguageConfig } from "../types.js"; + +// Verilog is treated as a SystemVerilog subset (same id) for the structural MVP. +export const systemverilogConfig = { + id: "systemverilog", + displayName: "SystemVerilog", + extensions: [".sv", ".svh", ".v", ".vh"], + concepts: [ + "module", "interface", "package", "program", "class", + "always_ff", "always_comb", "parameter", "generate", + "uvm", "uvm component", "uvm object", "factory", "phases", + "sequence", "sequencer", "driver", "monitor", "scoreboard", + "agent", "env", "tlm", "config_db", "clocking block", "assertion", + ], + filePatterns: { + entryPoints: ["top.sv", "tb_top.sv", "testbench.sv"], + barrels: [], + tests: ["*_test.sv", "*_tb.sv"], + config: ["*_pkg.sv", "*_pkg.svh"], + }, +} satisfies LanguageConfig; diff --git a/understand-anything-plugin/packages/core/src/plugins/parsers/__tests__/systemverilog-parser.test.ts b/understand-anything-plugin/packages/core/src/plugins/parsers/__tests__/systemverilog-parser.test.ts new file mode 100644 index 00000000..c6384f05 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/plugins/parsers/__tests__/systemverilog-parser.test.ts @@ -0,0 +1,95 @@ +import { describe, it, expect } from "vitest"; +import { stripSvComments, analyzeHdl } from "../systemverilog-parser.js"; + +describe("stripSvComments", () => { + it("removes // and /* */ but preserves newlines (line numbers stable)", () => { + const src = `module a; // trailing\n/* block\ncomment */ logic x;\nendmodule\n`; + const out = stripSvComments(src); + expect(out).not.toContain("trailing"); + expect(out).not.toContain("block"); + expect(out.split("\n").length).toBe(src.split("\n").length); + }); +}); + +describe("analyzeHdl — modules", () => { + const src = ` +package math_pkg; typedef enum {A,B} e_t; endpackage +module alu #(parameter int W=8) (input logic [W-1:0] a, output logic [W-1:0] y); + assign y = a; +endmodule +module top (input logic clk); + import math_pkg::*; + alu #(.W(16)) u_alu (.a(a), .y(y)); + fifo u_fifo (.clk(clk)); +endmodule`; + const r = analyzeHdl(src); + it("finds modules", () => expect(r.modules.map((m) => m.name).sort()).toEqual(["alu", "top"])); + it("finds the package", () => expect(r.packages.map((p) => p.name)).toContain("math_pkg")); + it("captures top's instantiations", () => { + const top = r.modules.find((m) => m.name === "top")!; + expect(top.instantiations.map((x) => x.type).sort()).toEqual(["alu", "fifo"]); + }); + it("captures package import", () => expect(r.imports.map((x) => x.pkg)).toContain("math_pkg")); + it("captures alu params and ports", () => { + const alu = r.modules.find((m) => m.name === "alu")!; + expect(alu.params).toContain("W"); + expect(alu.ports).toEqual(expect.arrayContaining(["a", "y"])); + }); +}); + +describe("analyzeHdl — classes/UVM", () => { + const src = ` +class my_item extends uvm_sequence_item; endclass +class my_driver extends uvm_driver #(my_item); + task run_phase(uvm_phase phase); endtask +endclass`; + const r = analyzeHdl(src); + it("captures base classes without param spec", () => { + expect(r.classes.find((c) => c.name === "my_item")?.base).toBe("uvm_sequence_item"); + expect(r.classes.find((c) => c.name === "my_driver")?.base).toBe("uvm_driver"); + }); + it("captures task as function entry", () => expect(r.functions.map((f) => f.name)).toContain("run_phase")); +}); + +describe("analyzeHdl — UVM composition + TLM (type_id::create / connect)", () => { + const src = ` +class dut_env extends uvm_env; + dut_agent agt; + dut_scoreboard sb; + function void build_phase(uvm_phase phase); + agt = dut_agent::type_id::create("agt", this); + sb = dut_scoreboard::type_id::create("sb", this); + endfunction + function void connect_phase(uvm_phase phase); + agt.mon.ap.connect(sb.imp); + endfunction +endclass +class dut_base_test extends uvm_test; + dut_env env; + function void build_phase(uvm_phase phase); + env = dut_env::type_id::create("env", this); + endfunction + task run_phase(uvm_phase phase); + dut_sequence seq; + seq = dut_sequence::type_id::create("seq"); + endtask +endclass`; + const r = analyzeHdl(src); + const env = r.classes.find((c) => c.name === "dut_env")!; + const test = r.classes.find((c) => c.name === "dut_base_test")!; + + it("captures created component types (handle + type)", () => + expect(env.creates.map((x) => x.type).sort()).toEqual(["dut_agent", "dut_scoreboard"])); + + it("captures the create handle names", () => + expect(env.creates.map((x) => x.handle).sort()).toEqual(["agt", "sb"])); + + it("captures creates from any phase/task body within the class span", () => + expect(test.creates.map((x) => x.type).sort()).toEqual(["dut_env", "dut_sequence"])); + + it("captures TLM connect endpoints as raw dotted chains", () => + expect(env.connects).toEqual([{ from: "agt.mon.ap", to: "sb.imp" }])); + + it("leaves connects empty when a class has no TLM wiring", () => + expect(test.connects).toEqual([])); +}); diff --git a/understand-anything-plugin/packages/core/src/plugins/parsers/__tests__/systemverilog-treesitter.test.ts b/understand-anything-plugin/packages/core/src/plugins/parsers/__tests__/systemverilog-treesitter.test.ts new file mode 100644 index 00000000..ca3995f5 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/plugins/parsers/__tests__/systemverilog-treesitter.test.ts @@ -0,0 +1,44 @@ +import { describe, it, expect } from "vitest"; +import { analyzeHdlTreeSitter } from "../systemverilog-treesitter.js"; + +describe("analyzeHdlTreeSitter (tree-sitter backend)", () => { + it("extracts UVM class base, factory composition, and TLM connects from the AST", async () => { + const src = `class dut_env extends uvm_env; + dut_agent agt; + dut_scoreboard sb; + function void build_phase(uvm_phase phase); + agt = dut_agent::type_id::create("agt", this); + sb = dut_scoreboard::type_id::create("sb", this); + endfunction + function void connect_phase(uvm_phase phase); + agt.mon.ap.connect(sb.imp); + endfunction +endclass`; + const h = await analyzeHdlTreeSitter(src); + const env = h.classes.find((c) => c.name === "dut_env")!; + expect(env.base).toBe("uvm_env"); + expect(env.creates.map((c) => c.type).sort()).toEqual(["dut_agent", "dut_scoreboard"]); + expect(env.creates.map((c) => c.handle).sort()).toEqual(["agt", "sb"]); + expect(env.connects).toEqual([{ from: "agt.mon.ap", to: "sb.imp" }]); + }); + + it("extracts modules with ports, params, and instantiations from the AST", async () => { + const src = `module top #(parameter int W = 8) (input logic clk, input logic rst_n); + alu #(.W(16)) u_alu (.a(a), .y(y)); + fifo u_fifo (.clk(clk)); +endmodule`; + const h = await analyzeHdlTreeSitter(src); + const top = h.modules.find((m) => m.name === "top")!; + expect(top.kind).toBe("module"); + expect(top.params).toContain("W"); + expect(top.ports).toEqual(expect.arrayContaining(["clk", "rst_n"])); + expect(top.instantiations.map((i) => i.type).sort()).toEqual(["alu", "fifo"]); + }); + + it("parses the UVM factory macro without error and finds the class", async () => { + const src = "class dut_seq_item extends uvm_sequence_item;\n `uvm_object_utils(dut_seq_item)\nendclass"; + const h = await analyzeHdlTreeSitter(src); + expect(h.classes.map((c) => c.name)).toContain("dut_seq_item"); + expect(h.classes.find((c) => c.name === "dut_seq_item")?.base).toBe("uvm_sequence_item"); + }); +}); diff --git a/understand-anything-plugin/packages/core/src/plugins/parsers/index.ts b/understand-anything-plugin/packages/core/src/plugins/parsers/index.ts index 58320912..9115aae7 100644 --- a/understand-anything-plugin/packages/core/src/plugins/parsers/index.ts +++ b/understand-anything-plugin/packages/core/src/plugins/parsers/index.ts @@ -10,6 +10,7 @@ export { ProtobufParser } from "./protobuf-parser.js"; export { TerraformParser } from "./terraform-parser.js"; export { MakefileParser } from "./makefile-parser.js"; export { ShellParser } from "./shell-parser.js"; +export { SystemVerilogParser } from "./systemverilog-parser.js"; import type { PluginRegistry } from "../registry.js"; import { MarkdownParser } from "./markdown-parser.js"; @@ -24,6 +25,7 @@ import { ProtobufParser } from "./protobuf-parser.js"; import { TerraformParser } from "./terraform-parser.js"; import { MakefileParser } from "./makefile-parser.js"; import { ShellParser } from "./shell-parser.js"; +import { SystemVerilogParser } from "./systemverilog-parser.js"; /** * Register all built-in non-code parsers with a PluginRegistry. @@ -41,4 +43,5 @@ export function registerAllParsers(registry: PluginRegistry): void { registry.register(new TerraformParser()); registry.register(new MakefileParser()); registry.register(new ShellParser()); + registry.register(new SystemVerilogParser()); } diff --git a/understand-anything-plugin/packages/core/src/plugins/parsers/systemverilog-parser.ts b/understand-anything-plugin/packages/core/src/plugins/parsers/systemverilog-parser.ts new file mode 100644 index 00000000..ce40af1e --- /dev/null +++ b/understand-anything-plugin/packages/core/src/plugins/parsers/systemverilog-parser.ts @@ -0,0 +1,211 @@ +// SystemVerilog / Verilog structural parser (custom, non-tree-sitter). +// +// Exposes a rich `analyzeHdl()` (used by the deterministic HDL graph assembler) +// and a `SystemVerilogParser` AnalyzerPlugin (used by UA's normal pipeline). +// Cross-symbol edge resolution (instantiation -> module, extends -> base) is the +// assembler's job; `analyzeHdl` only reports raw per-file facts. + +import type { AnalyzerPlugin, StructuralAnalysis, DefinitionInfo } from "../../types.js"; + +export interface HdlInstantiation { type: string; instance: string; line: number; } +export interface HdlModule { + name: string; + kind: "module" | "interface" | "program"; + lineRange: [number, number]; + ports: string[]; + params: string[]; + instantiations: HdlInstantiation[]; +} +/** A UVM component/object constructed via the factory: `handle = Type::type_id::create(...)`. */ +export interface HdlCreate { handle?: string; type: string; } +/** A raw TLM `lhs.connect(rhs)` call; chains are dotted handle paths resolved by the assembler. */ +export interface HdlConnect { from: string; to: string; } +export interface HdlClass { + name: string; + base?: string; + lineRange: [number, number]; + /** Component/object types this class constructs via the UVM factory (composition / "has-a"). */ + creates: HdlCreate[]; + /** TLM port/export wiring (`a.b.connect(c.d)`) captured as raw handle chains. */ + connects: HdlConnect[]; +} +export interface HdlPackage { name: string; lineRange: [number, number]; } +export interface HdlFunction { name: string; kind: "function" | "task"; lineRange: [number, number]; } +export interface HdlImport { pkg: string; line: number; } +export interface HdlAnalysis { + modules: HdlModule[]; + classes: HdlClass[]; + packages: HdlPackage[]; + functions: HdlFunction[]; + imports: HdlImport[]; +} + +/** Replace // and block comments + string literals with spaces, preserving newlines so line numbers stay stable. */ +export function stripSvComments(src: string): string { + let out = ""; + let i = 0; + const n = src.length; + while (i < n) { + const c = src[i], d = src[i + 1]; + if (c === "/" && d === "/") { + while (i < n && src[i] !== "\n") { out += " "; i++; } + } else if (c === "/" && d === "*") { + out += " "; i += 2; + while (i < n && !(src[i] === "*" && src[i + 1] === "/")) { out += src[i] === "\n" ? "\n" : " "; i++; } + out += " "; i += 2; + } else if (c === '"') { + out += " "; i++; + while (i < n && src[i] !== '"') { + if (src[i] === "\\") { out += " "; i += 2; } + else { out += src[i] === "\n" ? "\n" : " "; i++; } + } + out += " "; i++; + } else { + out += c; i++; + } + } + return out; +} + +const KEYWORDS = new Set([ + "module", "macromodule", "endmodule", "interface", "endinterface", "program", "endprogram", + "package", "endpackage", "class", "endclass", "extends", "function", "endfunction", "task", "endtask", + "begin", "end", "if", "else", "for", "while", "do", "foreach", "case", "endcase", "generate", "endgenerate", + "assign", "always", "always_ff", "always_comb", "always_latch", "initial", "import", "typedef", "enum", + "struct", "union", "logic", "wire", "reg", "bit", "int", "integer", "input", "output", "inout", "parameter", + "localparam", "return", "new", "virtual", "pure", "static", "automatic", "const", "ref", "var", "void", +]); + +const lineAt = (src: string, idx: number): number => src.slice(0, idx).split("\n").length; + +function extractParams(header: string): string[] { + const out: string[] = []; + const re = /\bparameter\b[^;,)]*?\b([A-Za-z_]\w*)\s*=/g; + let m: RegExpExecArray | null; + while ((m = re.exec(header))) out.push(m[1]); + return out; +} + +function extractPorts(header: string): string[] { + const out: string[] = []; + const re = /\b(?:input|output|inout)\b[^;,()]*?\b([A-Za-z_]\w*)\s*(?=[,)])/g; + let m: RegExpExecArray | null; + while ((m = re.exec(header))) out.push(m[1]); + return out; +} + +/** UVM factory constructions in a class body: `handle = Type::type_id::create(...)`. */ +function extractCreates(body: string): HdlCreate[] { + const out: HdlCreate[] = []; + const seen = new Set(); + // Optional `handle =` capture; `Type::type_id::create` is the factory signature. + const re = /(?:([A-Za-z_]\w*)\s*=\s*)?\b([A-Za-z_]\w*)\s*::\s*type_id\s*::\s*create\b/g; + let m: RegExpExecArray | null; + while ((m = re.exec(body))) { + const handle = m[1]; + const type = m[2]; + const key = `${handle ?? ""}:${type}`; + if (seen.has(key)) continue; + seen.add(key); + out.push({ handle, type }); + } + return out; +} + +/** TLM wiring in a class body: `a.b.c.connect(d.e)` -> raw dotted handle chains. */ +function extractConnects(body: string): HdlConnect[] { + const out: HdlConnect[] = []; + const re = /([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\.connect\s*\(\s*([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\s*\)/g; + let m: RegExpExecArray | null; + while ((m = re.exec(body))) out.push({ from: m[1], to: m[2] }); + return out; +} + +export function analyzeHdl(rawSrc: string): HdlAnalysis { + const src = stripSvComments(rawSrc); + const modules: HdlModule[] = []; + const classes: HdlClass[] = []; + const packages: HdlPackage[] = []; + const functions: HdlFunction[] = []; + const imports: HdlImport[] = []; + let m: RegExpExecArray | null; + + // Packages + const pkgRe = /\bpackage\s+([A-Za-z_]\w*)\b([\s\S]*?)\bendpackage\b/g; + while ((m = pkgRe.exec(src))) { + packages.push({ name: m[1], lineRange: [lineAt(src, m.index), lineAt(src, m.index + m[0].length)] }); + } + + // Classes (optional extends; strip param spec from base). Group 3 captures the + // class body so we can mine UVM composition (factory creates) and TLM wiring. + const classRe = /\bclass\s+([A-Za-z_]\w*)\s*(?:#\s*\([\s\S]*?\)\s*)?(?:extends\s+([A-Za-z_][\w:]*)\s*(?:#\s*\([\s\S]*?\)\s*)?)?([\s\S]*?)\bendclass\b/g; + while ((m = classRe.exec(src))) { + classes.push({ + name: m[1], + base: m[2] ? m[2].replace(/::$/, "") : undefined, + lineRange: [lineAt(src, m.index), lineAt(src, m.index + m[0].length)], + creates: extractCreates(m[3]), + connects: extractConnects(m[3]), + }); + } + + // Functions / tasks + const fnRe = /\b(function|task)\b\s+(?:automatic\s+|static\s+|virtual\s+)*(?:[A-Za-z_][\w:$]*\s+)*?([A-Za-z_]\w*)\s*[(;]/g; + while ((m = fnRe.exec(src))) { + functions.push({ name: m[2], kind: m[1] as "function" | "task", lineRange: [lineAt(src, m.index), lineAt(src, m.index)] }); + } + + // Package imports + const impRe = /\bimport\s+([A-Za-z_]\w*)\s*::/g; + while ((m = impRe.exec(src))) { + imports.push({ pkg: m[1], line: lineAt(src, m.index) }); + } + + // Modules / interfaces / programs (+ header ports/params + candidate instantiations) + const modRe = /\b(module|macromodule|interface|program)\s+([A-Za-z_]\w*)([\s\S]*?)\bend(?:module|interface|program)\b/g; + while ((m = modRe.exec(src))) { + const kind = (m[1] === "macromodule" ? "module" : m[1]) as "module" | "interface" | "program"; + const name = m[2]; + const body = m[3]; + const start = lineAt(src, m.index); + const end = lineAt(src, m.index + m[0].length); + const semi = body.indexOf(";"); + const header = semi >= 0 ? body.slice(0, semi) : body; + const inner = semi >= 0 ? body.slice(semi + 1) : ""; + const params = extractParams(header); + const ports = extractPorts(header); + const instantiations: HdlInstantiation[] = []; + // Require real separation between Type and instance: a #(...) param block OR whitespace. + // Without this, `clk(` backtracks into type="cl", instance="k" (false positive). + const instRe = /\b([A-Za-z_]\w*)(?:\s*#\s*\([\s\S]*?\)\s*|\s+)([A-Za-z_]\w*)\s*\(/g; + let im: RegExpExecArray | null; + while ((im = instRe.exec(inner))) { + const type = im[1], instance = im[2]; + if (KEYWORDS.has(type) || KEYWORDS.has(instance)) continue; + instantiations.push({ type, instance, line: start + (inner.slice(0, im.index).split("\n").length - 1) }); + } + modules.push({ name, kind, lineRange: [start, end], ports, params, instantiations }); + } + + return { modules, classes, packages, functions, imports }; +} + +export class SystemVerilogParser implements AnalyzerPlugin { + name = "systemverilog-parser"; + languages = ["systemverilog"]; + + analyzeFile(_filePath: string, content: string): StructuralAnalysis { + const h = analyzeHdl(content); + const definitions: DefinitionInfo[] = [ + ...h.modules.map((mod) => ({ name: mod.name, kind: mod.kind, lineRange: mod.lineRange, fields: mod.ports })), + ...h.packages.map((p) => ({ name: p.name, kind: "package", lineRange: p.lineRange, fields: [] as string[] })), + ]; + return { + functions: h.functions.map((f) => ({ name: f.name, lineRange: f.lineRange, params: [] as string[] })), + classes: h.classes.map((c) => ({ name: c.name, lineRange: c.lineRange, methods: [] as string[], properties: [] as string[] })), + imports: h.imports.map((i) => ({ source: i.pkg, specifiers: ["*"], lineNumber: i.line })), + exports: [], + definitions, + }; + } +} diff --git a/understand-anything-plugin/packages/core/src/plugins/parsers/systemverilog-treesitter.ts b/understand-anything-plugin/packages/core/src/plugins/parsers/systemverilog-treesitter.ts new file mode 100644 index 00000000..2b66ebf9 --- /dev/null +++ b/understand-anything-plugin/packages/core/src/plugins/parsers/systemverilog-treesitter.ts @@ -0,0 +1,179 @@ +// Tree-sitter SystemVerilog backend. +// +// Produces the same `HdlAnalysis` shape as the regex parser (systemverilog-parser.ts), +// but by walking the tree-sitter-systemverilog AST instead of regex heuristics — far +// more robust on real SV/UVM. The deterministic assembler (hdl-graph.ts) consumes +// either backend's output identically. +// +// Node-only: loads web-tree-sitter's WASM grammar via createRequire, so this module +// must never be imported from browser-facing code paths. + +import { createRequire } from "node:module"; +import type { + HdlAnalysis, HdlModule, HdlClass, HdlPackage, HdlFunction, HdlImport, + HdlInstantiation, HdlCreate, HdlConnect, +} from "./systemverilog-parser.js"; + +const require = createRequire(import.meta.url); +type SNode = import("web-tree-sitter").Node; + +// ── tiny AST helpers ────────────────────────────────────────────────────── +function eachNamed(n: SNode, fn: (c: SNode) => void): void { + for (let i = 0; i < n.namedChildCount; i++) { const c = n.namedChild(i); if (c) fn(c); } +} +function walk(n: SNode, visit: (x: SNode) => void): void { visit(n); eachNamed(n, (c) => walk(c, visit)); } +function collect(n: SNode, type: string): SNode[] { + const out: SNode[] = []; + walk(n, (x) => { if (x.type === type) out.push(x); }); + return out; +} +function childOfType(n: SNode | null, type: string): SNode | null { + if (!n) return null; + for (let i = 0; i < n.namedChildCount; i++) { const c = n.namedChild(i); if (c && c.type === type) return c; } + return null; +} +/** First `simple_identifier` in pre-order (the leftmost identifier of a subtree). */ +function firstIdent(n: SNode | null): string | undefined { + if (!n) return undefined; + let found: string | undefined; + walk(n, (x) => { if (found === undefined && x.type === "simple_identifier") found = x.text; }); + return found; +} +function idsIn(n: SNode | null): string[] { + return n ? collect(n, "simple_identifier").map((x) => x.text) : []; +} +const range = (n: SNode): [number, number] => [n.startPosition.row + 1, n.endPosition.row + 1]; + +// ── per-construct extraction ────────────────────────────────────────────── +/** `handle = Type::type_id::create(...)` -> { handle, type } (composition). */ +function extractCreates(classNode: SNode): HdlCreate[] { + const out: HdlCreate[] = []; + const seen = new Set(); + for (const asg of collect(classNode, "operator_assignment")) { + const bodies = collect(asg, "method_call_body").map((b) => childOfType(b, "simple_identifier")?.text); + if (!(bodies.includes("type_id") && bodies.includes("create"))) continue; + const handle = firstIdent(childOfType(asg, "variable_lvalue")); + const type = firstIdent(childOfType(asg, "expression")); // receiver of type_id == created type + if (!type) continue; + const key = `${handle ?? ""}:${type}`; + if (seen.has(key)) continue; + seen.add(key); + out.push({ handle, type }); + } + return out; +} +/** `a.b.c.connect(d.e)` -> { from: "a.b.c", to: "d.e" } (TLM wiring). */ +function extractConnects(classNode: SNode): HdlConnect[] { + const out: HdlConnect[] = []; + for (const call of collect(classNode, "tf_call")) { + const ids = idsIn(childOfType(call, "hierarchical_identifier")); + if (ids.length < 2 || ids[ids.length - 1] !== "connect") continue; + const from = ids.slice(0, -1).join("."); + const argHid = collect(childOfType(call, "list_of_arguments") ?? call, "hierarchical_identifier")[0] ?? null; + const to = idsIn(argHid).join("."); + if (from && to) out.push({ from, to }); + } + return out; +} + +const MOD_KINDS: ReadonlyArray = [ + ["module_declaration", "module"], + ["interface_declaration", "interface"], + ["program_declaration", "program"], +]; + +/** Extract HDL facts from a parsed SystemVerilog syntax tree's root node. */ +export function analyzeHdlFromTree(root: SNode): HdlAnalysis { + const modules: HdlModule[] = []; + const classes: HdlClass[] = []; + const packages: HdlPackage[] = []; + const functions: HdlFunction[] = []; + const imports: HdlImport[] = []; + + for (const [declType, kind] of MOD_KINDS) { + for (const md of collect(root, declType)) { + const header = md.namedChild(0); + const name = childOfType(header, "simple_identifier")?.text ?? firstIdent(md); + if (!name) continue; + const params = collect(header ?? md, "param_assignment") + .map((pa) => childOfType(pa, "simple_identifier")?.text).filter((x): x is string => !!x); + const ports = collect(header ?? md, "ansi_port_declaration") + .map((apd) => childOfType(apd, "simple_identifier")?.text).filter((x): x is string => !!x); + const instantiations: HdlInstantiation[] = collect(md, "module_instantiation") + .map((mi) => ({ + type: childOfType(mi, "simple_identifier")?.text ?? "", + instance: firstIdent(childOfType(mi, "hierarchical_instance")) ?? "", + line: mi.startPosition.row + 1, + })) + .filter((i) => i.type.length > 0); + modules.push({ name, kind, lineRange: range(md), ports, params, instantiations }); + } + } + + for (const cd of collect(root, "class_declaration")) { + const name = childOfType(cd, "simple_identifier")?.text; + if (!name) continue; + const base = childOfType(childOfType(cd, "class_type"), "simple_identifier")?.text; + classes.push({ name, base, lineRange: range(cd), creates: extractCreates(cd), connects: extractConnects(cd) }); + } + + for (const pd of collect(root, "package_declaration")) { + const name = childOfType(pd, "simple_identifier")?.text; + if (name) packages.push({ name, lineRange: range(pd) }); + } + + for (const fd of collect(root, "function_declaration")) { + const name = childOfType(childOfType(fd, "function_body_declaration"), "simple_identifier")?.text; + if (name) functions.push({ name, kind: "function", lineRange: [fd.startPosition.row + 1, fd.startPosition.row + 1] }); + } + for (const td of collect(root, "task_declaration")) { + const name = firstIdent(childOfType(td, "task_body_declaration") ?? td); + if (name) functions.push({ name, kind: "task", lineRange: [td.startPosition.row + 1, td.startPosition.row + 1] }); + } + for (const cc of collect(root, "class_constructor_declaration")) { + functions.push({ name: "new", kind: "function", lineRange: [cc.startPosition.row + 1, cc.startPosition.row + 1] }); + } + + for (const pii of collect(root, "package_import_item")) { + const name = childOfType(pii, "simple_identifier")?.text; + if (name) imports.push({ pkg: name, line: pii.startPosition.row + 1 }); + } + + return { modules, classes, packages, functions, imports }; +} + +// ── lazy WASM grammar loader ────────────────────────────────────────────── +let _parser: Promise | null = null; +function getParser(): Promise { + if (!_parser) { + _parser = (async () => { + const mod = await import("web-tree-sitter"); + const ParserCls = mod.Parser; + const LanguageCls = mod.Language; + await ParserCls.init(); + const wasmPath = require.resolve("tree-sitter-systemverilog/tree-sitter-systemverilog.wasm"); + const lang = await LanguageCls.load(wasmPath); + const p = new ParserCls(); + p.setLanguage(lang); + return p; + })(); + } + return _parser; +} + +/** Parse one SV/Verilog source string and extract HDL facts via the tree-sitter AST. */ +export async function analyzeHdlTreeSitter(src: string): Promise { + const parser = await getParser(); + const tree = parser.parse(src); + if (!tree) return { modules: [], classes: [], packages: [], functions: [], imports: [] }; + try { + return analyzeHdlFromTree(tree.rootNode); + } finally { + tree.delete?.(); + } +} + +/** Whether the tree-sitter SystemVerilog grammar can be loaded in this environment. */ +export async function isSvTreeSitterAvailable(): Promise { + try { await getParser(); return true; } catch { return false; } +} diff --git a/understand-anything-plugin/pnpm-lock.yaml b/understand-anything-plugin/pnpm-lock.yaml index 16b6eed8..9d3489ea 100644 --- a/understand-anything-plugin/pnpm-lock.yaml +++ b/understand-anything-plugin/pnpm-lock.yaml @@ -11,6 +11,12 @@ importers: '@understand-anything/core': specifier: workspace:* version: link:packages/core + graphology: + specifier: ~0.26.0 + version: 0.26.0(graphology-types@0.24.8) + graphology-communities-louvain: + specifier: ^2.0.2 + version: 2.0.2(graphology-types@0.24.8) devDependencies: '@types/node': specifier: ^22.0.0 @@ -57,6 +63,9 @@ importers: tree-sitter-rust: specifier: ^0.24.0 version: 0.24.0 + tree-sitter-systemverilog: + specifier: ^0.3.1 + version: 0.3.1 tree-sitter-typescript: specifier: ^0.23.2 version: 0.23.2 @@ -133,7 +142,7 @@ importers: devDependencies: '@tailwindcss/vite': specifier: ^4.0.0 - version: 4.2.2(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3)) + version: 4.2.2(vite@6.4.3(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3)) '@types/d3-force': specifier: ^3.0.10 version: 3.0.10 @@ -145,7 +154,7 @@ importers: version: 19.2.3(@types/react@19.2.14) '@vitejs/plugin-react': specifier: ^4.3.0 - version: 4.7.0(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3)) + version: 4.7.0(vite@6.4.3(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3)) '@vitest/coverage-v8': specifier: ^3.2.4 version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3)) @@ -156,8 +165,8 @@ importers: specifier: ^5.7.0 version: 5.9.3 vite: - specifier: ^6.0.0 - version: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3) + specifier: ^6.4.2 + version: 6.4.3(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3) vitest: specifier: ^3.1.0 version: 3.2.4(@types/debug@4.1.13)(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3) @@ -421,6 +430,10 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + '@istanbuljs/schema@0.1.3': resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} @@ -441,6 +454,14 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@npmcli/agent@3.0.0': + resolution: {integrity: sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@npmcli/fs@4.0.0': + resolution: {integrity: sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==} + engines: {node: ^18.17.0 || >=20.5.0} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -745,6 +766,7 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + deprecated: Potential CWE-502 - Update to 1.3.1 or higher '@vitejs/plugin-react@4.7.0': resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} @@ -799,6 +821,14 @@ packages: '@xyflow/system@0.0.75': resolution: {integrity: sha512-iXs+AGFLi8w/VlAoc/iSxk+CxfT6o64Uw/k0CKASOPqjqz6E0rb5jFZgJtXGZCpfQI6OQpu5EnumP5fGxQheaQ==} + abbrev@3.0.1: + resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==} + engines: {node: ^18.17.0 || >=20.5.0} + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -853,6 +883,10 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + cacache@19.0.1: + resolution: {integrity: sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==} + engines: {node: ^18.17.0 || >=20.5.0} + caniuse-lite@1.0.30001781: resolution: {integrity: sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==} @@ -879,6 +913,10 @@ packages: resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} engines: {node: '>= 16'} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + classcat@5.0.5: resolution: {integrity: sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==} @@ -994,10 +1032,20 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + enhanced-resolve@5.20.1: resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} engines: {node: '>=10.13.0'} + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + err-code@2.0.3: + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} @@ -1024,6 +1072,9 @@ packages: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} + exponential-backoff@3.1.3: + resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} + extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -1040,6 +1091,10 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} + fs-minipass@3.0.3: + resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1084,6 +1139,11 @@ packages: peerDependencies: graphology-types: '>=0.24.0' + graphology@0.26.0: + resolution: {integrity: sha512-8SSImzgUUYC89Z042s+0r/vMibY7GX/Emz4LDO5e7jYXhuoWfHISPFJYjpRLUSJGq6UQ6xlenvX1p/hJdfXuXg==} + peerDependencies: + graphology-types: '>=0.24.0' + has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -1100,13 +1160,36 @@ packages: html-url-attributes@3.0.1: resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + http-cache-semantics@4.2.0: + resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + ignore@7.0.5: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + inline-style-parser@0.2.7: resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + ip-address@10.2.0: + resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==} + engines: {node: '>= 12'} + is-alphabetical@2.0.1: resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} @@ -1130,6 +1213,10 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isexe@3.1.5: + resolution: {integrity: sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==} + engines: {node: '>=18'} + istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -1264,6 +1351,10 @@ packages: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} + make-fetch-happen@14.0.3: + resolution: {integrity: sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==} + engines: {node: ^18.17.0 || >=20.5.0} + mdast-util-from-markdown@2.0.3: resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} @@ -1359,10 +1450,38 @@ packages: resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} engines: {node: '>=16 || 14 >=14.17'} + minipass-collect@2.0.1: + resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass-fetch@4.0.1: + resolution: {integrity: sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + minipass-flush@1.0.7: + resolution: {integrity: sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==} + engines: {node: '>= 8'} + + minipass-pipeline@1.2.4: + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} + + minipass-sized@1.0.3: + resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} + engines: {node: '>=8'} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + minipass@7.1.3: resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + mnemonist@0.39.8: resolution: {integrity: sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ==} @@ -1374,6 +1493,10 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + node-addon-api@8.7.0: resolution: {integrity: sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==} engines: {node: ^18 || ^20 || >= 21} @@ -1382,12 +1505,26 @@ packages: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true + node-gyp@11.5.0: + resolution: {integrity: sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + node-releases@2.0.36: resolution: {integrity: sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==} + nopt@8.1.0: + resolution: {integrity: sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + obliterator@2.0.5: resolution: {integrity: sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==} + p-map@7.0.4: + resolution: {integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==} + engines: {node: '>=18'} + package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} @@ -1428,6 +1565,14 @@ packages: peerDependencies: react: '>=16.0.0' + proc-log@5.0.0: + resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + promise-retry@2.0.1: + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} + property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} @@ -1456,11 +1601,18 @@ packages: remark-rehype@11.1.2: resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + rollup@4.60.0: resolution: {integrity: sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} @@ -1488,6 +1640,18 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + socks-proxy-agent@8.0.5: + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} + engines: {node: '>= 14'} + + socks@2.8.9: + resolution: {integrity: sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -1495,6 +1659,10 @@ packages: space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + ssri@12.0.0: + resolution: {integrity: sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==} + engines: {node: ^18.17.0 || >=20.5.0} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -1540,6 +1708,10 @@ packages: resolution: {integrity: sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==} engines: {node: '>=6'} + tar@7.5.16: + resolution: {integrity: sha512-56adEpPMouktRlBLXiaYFFzZ/3+JXa8P9n7WbR+ibIjtviN55mEaOkiysCnPnWm+7kkui1Dn8J9l+g6zV8731w==} + engines: {node: '>=18'} + test-exclude@7.0.2: resolution: {integrity: sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==} engines: {node: '>=18'} @@ -1654,6 +1826,14 @@ packages: tree-sitter: optional: true + tree-sitter-systemverilog@0.3.1: + resolution: {integrity: sha512-1YjtQJhGHm3LyLO5W31FkAwgBnyO1QvIrb8qGQYIIKwGCO3oqBuq/SJOnY4GA6DgSkJK2etKVYZ1i+Rz22OYbA==} + peerDependencies: + tree-sitter: ^0.25.0 + peerDependenciesMeta: + tree-sitter: + optional: true + tree-sitter-typescript@0.23.2: resolution: {integrity: sha512-e04JUUKxTT53/x3Uq1zIL45DoYKVfHH4CZqwgZhPg5qYROl5nQjV+85ruFzFGZxu+QeFVbRTPDRnqL9UbU4VeA==} peerDependencies: @@ -1682,6 +1862,14 @@ packages: unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + unique-filename@4.0.0: + resolution: {integrity: sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + unique-slug@5.0.0: + resolution: {integrity: sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==} + engines: {node: ^18.17.0 || >=20.5.0} + unist-util-is@6.0.1: resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} @@ -1759,6 +1947,46 @@ packages: yaml: optional: true + vite@6.4.3: + resolution: {integrity: sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + vitest@3.2.4: resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -1795,6 +2023,11 @@ packages: engines: {node: '>= 8'} hasBin: true + which@5.0.0: + resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} @@ -1811,6 +2044,13 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + yaml@2.8.3: resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} engines: {node: '>= 14.6'} @@ -2069,6 +2309,10 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.3 + '@istanbuljs/schema@0.1.3': {} '@jridgewell/gen-mapping@0.3.13': @@ -2090,6 +2334,20 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@npmcli/agent@3.0.0': + dependencies: + agent-base: 7.1.4 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + lru-cache: 10.4.3 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + + '@npmcli/fs@4.0.0': + dependencies: + semver: 7.7.4 + '@pkgjs/parseargs@0.11.0': optional: true @@ -2231,12 +2489,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.2.2 '@tailwindcss/oxide-win32-x64-msvc': 4.2.2 - '@tailwindcss/vite@4.2.2(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3))': + '@tailwindcss/vite@4.2.2(vite@6.4.3(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3))': dependencies: '@tailwindcss/node': 4.2.2 '@tailwindcss/oxide': 4.2.2 tailwindcss: 4.2.2 - vite: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3) + vite: 6.4.3(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3) '@types/babel__core@7.20.5': dependencies: @@ -2333,7 +2591,7 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3))': + '@vitejs/plugin-react@4.7.0(vite@6.4.3(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) @@ -2341,7 +2599,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.27 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3) + vite: 6.4.3(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3) transitivePeerDependencies: - supports-color @@ -2429,6 +2687,10 @@ snapshots: d3-selection: 3.0.0 d3-zoom: 3.0.0 + abbrev@3.0.1: {} + + agent-base@7.1.4: {} + ansi-regex@5.0.1: {} ansi-regex@6.2.2: {} @@ -2473,6 +2735,21 @@ snapshots: cac@6.7.14: {} + cacache@19.0.1: + dependencies: + '@npmcli/fs': 4.0.0 + fs-minipass: 3.0.3 + glob: 10.5.0 + lru-cache: 10.4.3 + minipass: 7.1.3 + minipass-collect: 2.0.1 + minipass-flush: 1.0.7 + minipass-pipeline: 1.2.4 + p-map: 7.0.4 + ssri: 12.0.0 + tar: 7.5.16 + unique-filename: 4.0.0 + caniuse-lite@1.0.30001781: {} ccount@2.0.1: {} @@ -2495,6 +2772,8 @@ snapshots: check-error@2.1.3: {} + chownr@3.0.0: {} + classcat@5.0.5: {} clsx@2.1.1: {} @@ -2589,11 +2868,20 @@ snapshots: emoji-regex@9.2.2: {} + encoding@0.1.13: + dependencies: + iconv-lite: 0.6.3 + optional: true + enhanced-resolve@5.20.1: dependencies: graceful-fs: 4.2.11 tapable: 2.3.2 + env-paths@2.2.1: {} + + err-code@2.0.3: {} + es-module-lexer@1.7.0: {} esbuild@0.25.12: @@ -2637,6 +2925,8 @@ snapshots: expect-type@1.3.0: {} + exponential-backoff@3.1.3: {} + extend@3.0.2: {} fdir@6.5.0(picomatch@4.0.4): @@ -2648,6 +2938,10 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 + fs-minipass@3.0.3: + dependencies: + minipass: 7.1.3 + fsevents@2.3.3: optional: true @@ -2692,6 +2986,11 @@ snapshots: graphology-types: 0.24.8 obliterator: 2.0.5 + graphology@0.26.0(graphology-types@0.24.8): + dependencies: + events: 3.3.0 + graphology-types: 0.24.8 + has-flag@4.0.0: {} hast-util-to-jsx-runtime@2.3.6: @@ -2722,10 +3021,35 @@ snapshots: html-url-attributes@3.0.1: {} + http-cache-semantics@4.2.0: {} + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + optional: true + ignore@7.0.5: {} + imurmurhash@0.1.4: {} + inline-style-parser@0.2.7: {} + ip-address@10.2.0: {} + is-alphabetical@2.0.1: {} is-alphanumerical@2.0.1: @@ -2743,6 +3067,8 @@ snapshots: isexe@2.0.0: {} + isexe@3.1.5: {} + istanbul-lib-coverage@3.2.2: {} istanbul-lib-report@3.0.1: @@ -2855,6 +3181,22 @@ snapshots: dependencies: semver: 7.7.4 + make-fetch-happen@14.0.3: + dependencies: + '@npmcli/agent': 3.0.0 + cacache: 19.0.1 + http-cache-semantics: 4.2.0 + minipass: 7.1.3 + minipass-fetch: 4.0.1 + minipass-flush: 1.0.7 + minipass-pipeline: 1.2.4 + negotiator: 1.0.0 + proc-log: 5.0.0 + promise-retry: 2.0.1 + ssri: 12.0.0 + transitivePeerDependencies: + - supports-color + mdast-util-from-markdown@2.0.3: dependencies: '@types/mdast': 4.0.4 @@ -3085,8 +3427,40 @@ snapshots: dependencies: brace-expansion: 2.0.2 + minipass-collect@2.0.1: + dependencies: + minipass: 7.1.3 + + minipass-fetch@4.0.1: + dependencies: + minipass: 7.1.3 + minipass-sized: 1.0.3 + minizlib: 3.1.0 + optionalDependencies: + encoding: 0.1.13 + + minipass-flush@1.0.7: + dependencies: + minipass: 3.3.6 + + minipass-pipeline@1.2.4: + dependencies: + minipass: 3.3.6 + + minipass-sized@1.0.3: + dependencies: + minipass: 3.3.6 + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + minipass@7.1.3: {} + minizlib@3.1.0: + dependencies: + minipass: 7.1.3 + mnemonist@0.39.8: dependencies: obliterator: 2.0.5 @@ -3095,14 +3469,37 @@ snapshots: nanoid@3.3.11: {} + negotiator@1.0.0: {} + node-addon-api@8.7.0: {} node-gyp-build@4.8.4: {} + node-gyp@11.5.0: + dependencies: + env-paths: 2.2.1 + exponential-backoff: 3.1.3 + graceful-fs: 4.2.11 + make-fetch-happen: 14.0.3 + nopt: 8.1.0 + proc-log: 5.0.0 + semver: 7.7.4 + tar: 7.5.16 + tinyglobby: 0.2.15 + which: 5.0.0 + transitivePeerDependencies: + - supports-color + node-releases@2.0.36: {} + nopt@8.1.0: + dependencies: + abbrev: 3.0.1 + obliterator@2.0.5: {} + p-map@7.0.4: {} + package-json-from-dist@1.0.1: {} pandemonium@2.4.1: @@ -3146,6 +3543,13 @@ snapshots: clsx: 2.1.1 react: 19.2.4 + proc-log@5.0.0: {} + + promise-retry@2.0.1: + dependencies: + err-code: 2.0.3 + retry: 0.12.0 + property-information@7.1.0: {} react-dom@19.2.4(react@19.2.4): @@ -3192,6 +3596,8 @@ snapshots: unified: 11.0.5 vfile: 6.0.3 + retry@0.12.0: {} + rollup@4.60.0: dependencies: '@types/estree': 1.0.8 @@ -3223,6 +3629,9 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.60.0 fsevents: 2.3.3 + safer-buffer@2.1.2: + optional: true + scheduler@0.27.0: {} semver@6.3.1: {} @@ -3239,10 +3648,29 @@ snapshots: signal-exit@4.1.0: {} + smart-buffer@4.2.0: {} + + socks-proxy-agent@8.0.5: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + socks: 2.8.9 + transitivePeerDependencies: + - supports-color + + socks@2.8.9: + dependencies: + ip-address: 10.2.0 + smart-buffer: 4.2.0 + source-map-js@1.2.1: {} space-separated-tokens@2.0.2: {} + ssri@12.0.0: + dependencies: + minipass: 7.1.3 + stackback@0.0.2: {} std-env@3.10.0: {} @@ -3292,6 +3720,14 @@ snapshots: tapable@2.3.2: {} + tar@7.5.16: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.3 + minizlib: 3.1.0 + yallist: 5.0.0 + test-exclude@7.0.2: dependencies: '@istanbuljs/schema': 0.1.3 @@ -3369,6 +3805,14 @@ snapshots: node-addon-api: 8.7.0 node-gyp-build: 4.8.4 + tree-sitter-systemverilog@0.3.1: + dependencies: + node-addon-api: 8.7.0 + node-gyp: 11.5.0 + node-gyp-build: 4.8.4 + transitivePeerDependencies: + - supports-color + tree-sitter-typescript@0.23.2: dependencies: node-addon-api: 8.7.0 @@ -3395,6 +3839,14 @@ snapshots: trough: 2.2.0 vfile: 6.0.3 + unique-filename@4.0.0: + dependencies: + unique-slug: 5.0.0 + + unique-slug@5.0.0: + dependencies: + imurmurhash: 0.1.4 + unist-util-is@6.0.1: dependencies: '@types/unist': 3.0.3 @@ -3444,7 +3896,7 @@ snapshots: debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3) + vite: 6.4.3(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3) transitivePeerDependencies: - '@types/node' - jiti @@ -3465,7 +3917,7 @@ snapshots: debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3) + vite: 6.4.3(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3) transitivePeerDependencies: - '@types/node' - jiti @@ -3510,6 +3962,36 @@ snapshots: lightningcss: 1.32.0 yaml: 2.8.3 + vite@6.4.3(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.8 + rollup: 4.60.0 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 22.19.15 + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.32.0 + yaml: 2.8.3 + + vite@6.4.3(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.8 + rollup: 4.60.0 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 25.5.0 + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.32.0 + yaml: 2.8.3 + vitest@3.2.4(@types/debug@4.1.13)(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3): dependencies: '@types/chai': 5.2.3 @@ -3600,6 +4082,10 @@ snapshots: dependencies: isexe: 2.0.0 + which@5.0.0: + dependencies: + isexe: 3.1.5 + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 @@ -3619,6 +4105,10 @@ snapshots: yallist@3.1.1: {} + yallist@4.0.0: {} + + yallist@5.0.0: {} + yaml@2.8.3: {} zod@4.3.6: {} diff --git a/understand-anything-plugin/scripts/analyze-hdl.mjs b/understand-anything-plugin/scripts/analyze-hdl.mjs new file mode 100644 index 00000000..2cc9dedd --- /dev/null +++ b/understand-anything-plugin/scripts/analyze-hdl.mjs @@ -0,0 +1,46 @@ +// Deterministic HDL analyzer CLI: walk a project's Verilog/SystemVerilog files, +// assemble a knowledge graph, validate it, and write the dashboard JSON. +// +// Usage: node scripts/analyze-hdl.mjs +// -> writes /.understand-anything/knowledge-graph.json +import { readFileSync, readdirSync, mkdirSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; +import { buildHdlGraph, buildHdlGraphTreeSitter } from "../packages/core/dist/analyzer/hdl-graph.js"; +import { validateGraph } from "../packages/core/dist/schema.js"; + +const target = process.argv[2]; +if (!target) { + console.error("usage: node scripts/analyze-hdl.mjs "); + process.exit(1); +} + +const files = readdirSync(target, { recursive: true }) + .map((p) => String(p).replace(/\\/g, "/")) + .filter((p) => /\.(sv|svh|v|vh)$/.test(p)) + .map((p) => ({ path: p, content: readFileSync(join(target, p), "utf8") })); + +// Prefer the accurate tree-sitter backend; fall back to the regex parser if the +// SystemVerilog WASM grammar can't be loaded in this environment. +let graph; +try { + graph = await buildHdlGraphTreeSitter(files, "uvm_demo", "local"); + console.log("parser: tree-sitter-systemverilog"); +} catch (err) { + console.warn("tree-sitter unavailable, using regex fallback:", err?.message ?? err); + graph = buildHdlGraph(files, "uvm_demo", "local"); +} +const res = validateGraph(graph); +if (!res.success) { + console.error("graph invalid:", JSON.stringify(res.issues), res.fatal ?? ""); + process.exit(2); +} + +const outDir = join(target, ".understand-anything"); +mkdirSync(outDir, { recursive: true }); +const outFile = join(outDir, "knowledge-graph.json"); +writeFileSync(outFile, JSON.stringify(graph, null, 2)); + +const counts = graph.nodes.reduce((acc, n) => ((acc[n.type] = (acc[n.type] ?? 0) + 1), acc), {}); +console.log(`wrote ${graph.nodes.length} nodes / ${graph.edges.length} edges -> ${outFile}`); +console.log("node types:", JSON.stringify(counts)); +console.log("edge types:", JSON.stringify(graph.edges.reduce((a, e) => ((a[e.type] = (a[e.type] ?? 0) + 1), a), {})));