superacli 1.1.5 → 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1081) hide show
  1. package/README.md +77 -53
  2. package/__tests__/azd-plugin.test.js +109 -0
  3. package/__tests__/config.test.js +4 -3
  4. package/__tests__/discover.test.js +59 -0
  5. package/__tests__/goose-plugin.test.js +149 -0
  6. package/__tests__/help-json.test.js +2 -0
  7. package/__tests__/openhands-plugin.test.js +106 -0
  8. package/__tests__/plugin-cocoindex-code-uninstall.test.js +19 -0
  9. package/__tests__/plugin-cocoindex-code.test.js +37 -0
  10. package/__tests__/plugin-install-guidance.test.js +81 -0
  11. package/__tests__/plugins-registry.test.js +44 -0
  12. package/__tests__/plugins-store.test.js +40 -5
  13. package/__tests__/process-adapter.test.js +50 -1
  14. package/__tests__/resend-plugin.test.js +22 -1
  15. package/__tests__/server-app.test.js +1 -0
  16. package/__tests__/server-routes-commands.test.js +20 -2
  17. package/__tests__/server-routes-plugins.test.js +130 -0
  18. package/__tests__/skills.test.js +26 -0
  19. package/__tests__/squirrelscan-plugin.test.js +129 -0
  20. package/__tests__/uipath-plugin.test.js +104 -0
  21. package/__tests__/uipathcli-plugin.test.js +95 -0
  22. package/cli/adapters/mcp.js +2 -0
  23. package/cli/adapters/process.js +49 -2
  24. package/cli/config.js +240 -3
  25. package/cli/discover.js +157 -0
  26. package/cli/help-json.js +16 -1
  27. package/cli/plugin-install-guidance.js +92 -37
  28. package/cli/plugins-manager.js +1 -0
  29. package/cli/plugins-registry.js +74 -8
  30. package/cli/plugins-store.js +78 -17
  31. package/cli/skills-mcp.js +1 -1
  32. package/cli/skills.js +39 -2
  33. package/cli/supercli.js +87 -11
  34. package/docs/feature-gaps.md +8 -8
  35. package/docs/features/azd-uipath-plugins.md +43 -0
  36. package/docs/features/server-plugins.md +62 -0
  37. package/docs/features/skills.md +9 -5
  38. package/docs/{supported-harnesses.md → plugins-available.md} +4 -3
  39. package/docs/{plugin-harness-guide.md → plugins-how-to.md} +1 -1
  40. package/docs/plugins.md +26 -20
  41. package/docs/server-plugins-usage-guide.md +182 -0
  42. package/docs/skills-catalog.md +12 -10
  43. package/package.json +1 -1
  44. package/plugins/agent-browser/README.md +69 -0
  45. package/plugins/agent-browser/plugin.json +111 -0
  46. package/plugins/agent-browser/skills/quickstart/SKILL.md +66 -0
  47. package/plugins/aider/README.md +53 -0
  48. package/plugins/aider/plugin.json +105 -0
  49. package/plugins/aider/scripts/aider-wrapper.js +243 -0
  50. package/plugins/aider/scripts/setup-aider.js +37 -0
  51. package/plugins/aider/skills/dry-run-review.md +24 -0
  52. package/plugins/aider/skills/model-and-provider.md +24 -0
  53. package/plugins/aider/skills/one-shot-edits.md +30 -0
  54. package/plugins/aider/skills/quickstart/SKILL.md +51 -0
  55. package/plugins/azd/README.md +28 -0
  56. package/plugins/azd/plugin.json +87 -0
  57. package/plugins/azd/skills/quickstart/SKILL.md +41 -0
  58. package/plugins/blogwatcher/README.md +3 -3
  59. package/plugins/boxlite/Dockerfile +9 -0
  60. package/plugins/boxlite/README.md +62 -0
  61. package/plugins/boxlite/plugin.json +201 -0
  62. package/plugins/boxlite/scripts/run-boxlite.js +106 -0
  63. package/plugins/boxlite/skills/quickstart/SKILL.md +40 -0
  64. package/plugins/cass/plugin.json +150 -0
  65. package/plugins/cass/scripts/setup-cass.js +47 -0
  66. package/plugins/cass/skills/quickstart/SKILL.md +46 -0
  67. package/plugins/clever/README.md +46 -0
  68. package/plugins/clever/plugin.json +119 -0
  69. package/plugins/clever/scripts/setup-clever.js +28 -0
  70. package/plugins/clever/skills/auth-and-profile.md +29 -0
  71. package/plugins/clever/skills/passthrough-safety.md +21 -0
  72. package/plugins/clever/skills/quickstart/SKILL.md +45 -0
  73. package/plugins/clever/skills/resource-inventory.md +24 -0
  74. package/plugins/clix/README.md +4 -4
  75. package/plugins/cocoindex-code/README.md +64 -0
  76. package/plugins/cocoindex-code/plugin.json +81 -0
  77. package/plugins/cocoindex-code/scripts/__pycache__/query.cpython-310.pyc +0 -0
  78. package/plugins/cocoindex-code/scripts/__pycache__/query.cpython-311.pyc +0 -0
  79. package/plugins/cocoindex-code/scripts/post-install.js +61 -0
  80. package/plugins/cocoindex-code/scripts/post-uninstall.js +25 -0
  81. package/plugins/cocoindex-code/scripts/query.py +88 -0
  82. package/plugins/cocoindex-code/scripts/run-query.js +50 -0
  83. package/plugins/cocoindex-code/skills/quickstart/SKILL.md +73 -0
  84. package/plugins/copilot/README.md +24 -0
  85. package/plugins/copilot/plugin.json +80 -0
  86. package/plugins/copilot/skills/quickstart/SKILL.md +44 -0
  87. package/plugins/gemini/README.md +24 -0
  88. package/plugins/gemini/plugin.json +98 -0
  89. package/plugins/gemini/skills/quickstart/SKILL.md +44 -0
  90. package/plugins/gifcap/plugin.json +119 -0
  91. package/plugins/gifcap/scripts/setup-gifcap.js +44 -0
  92. package/plugins/gifcap/skills/quickstart/SKILL.md +34 -0
  93. package/plugins/gifcap/test-record-quiet.gif +0 -0
  94. package/plugins/gifcap/test-record.gif +0 -0
  95. package/plugins/goose/README.md +36 -0
  96. package/plugins/goose/plugin.json +183 -0
  97. package/plugins/goose/skills/quickstart/SKILL.md +44 -0
  98. package/plugins/json-server/README.md +58 -0
  99. package/plugins/json-server/plugin.json +113 -0
  100. package/plugins/json-server/skills/quickstart/SKILL.md +57 -0
  101. package/plugins/lightpanda/README.md +145 -0
  102. package/plugins/lightpanda/package-lock.json +1375 -0
  103. package/plugins/lightpanda/package.json +12 -0
  104. package/plugins/lightpanda/plugin.json +116 -0
  105. package/plugins/lightpanda/scripts/lightpanda-contacts.js +494 -0
  106. package/plugins/lightpanda/scripts/lightpanda-generic-extract.js +403 -0
  107. package/plugins/lightpanda/scripts/lightpanda-wrapper.js +480 -0
  108. package/plugins/lightpanda/scripts/setup-lightpanda.js +39 -0
  109. package/plugins/lightpanda/skills/contact-discovery.md +51 -0
  110. package/plugins/lightpanda/skills/generic-extraction.md +66 -0
  111. package/plugins/lightpanda/skills/quickstart/SKILL.md +103 -0
  112. package/plugins/lightpanda/skills/resilient-navigation.md +42 -0
  113. package/plugins/monty/README.md +2 -2
  114. package/plugins/nullclaw/README.md +3 -3
  115. package/plugins/offline-ai/README.md +23 -0
  116. package/plugins/offline-ai/plugin.json +82 -0
  117. package/plugins/offline-ai/skills/quickstart/SKILL.md +43 -0
  118. package/plugins/openhands/README.md +25 -0
  119. package/plugins/openhands/plugin.json +116 -0
  120. package/plugins/openhands/skills/quickstart/SKILL.md +26 -0
  121. package/plugins/plandex/README.md +25 -0
  122. package/plugins/plandex/plugin.json +130 -0
  123. package/plugins/plandex/skills/quickstart/SKILL.md +50 -0
  124. package/plugins/plugins.json +190 -2
  125. package/plugins/resend/plugin.json +279 -2
  126. package/plugins/resend/skills/quickstart/SKILL.md +32 -13
  127. package/plugins/squirrelscan/Dockerfile +5 -0
  128. package/plugins/squirrelscan/README.md +47 -0
  129. package/plugins/squirrelscan/plugin.json +493 -0
  130. package/plugins/squirrelscan/scripts/post-install.js +33 -0
  131. package/plugins/squirrelscan/scripts/post-uninstall.js +25 -0
  132. package/plugins/squirrelscan/scripts/run-squirrel.js +73 -0
  133. package/plugins/squirrelscan/skills/audit-workflow/SKILL.md +33 -0
  134. package/plugins/squirrelscan/skills/publish-report/SKILL.md +33 -0
  135. package/plugins/squirrelscan/skills/quickstart/SKILL.md +41 -0
  136. package/plugins/uipath/README.md +27 -0
  137. package/plugins/uipath/plugin.json +86 -0
  138. package/plugins/uipath/skills/quickstart/SKILL.md +47 -0
  139. package/plugins/uipathcli/README.md +28 -0
  140. package/plugins/uipathcli/plugin.json +120 -0
  141. package/plugins/uipathcli/scripts/run-uipath-cli.js +49 -0
  142. package/plugins/uipathcli/skills/quickstart/SKILL.md +22 -0
  143. package/plugins/xurl/README.md +4 -4
  144. package/server/app.js +5 -2
  145. package/server/public/app.js +3 -0
  146. package/server/routes/commands.js +95 -12
  147. package/server/routes/plugins.js +262 -0
  148. package/server/services/pluginsService.js +303 -0
  149. package/server/views/command-edit.ejs +196 -14
  150. package/server/views/partials/head.ejs +1 -0
  151. package/server/views/plugins.ejs +264 -0
  152. package/tests/test-plugins-registry.js +30 -0
  153. package/tests/test-resend-smoke.sh +7 -3
  154. package/.beads/.br_history/issues.20260308_200823_636718328.jsonl +0 -20
  155. package/.beads/.br_history/issues.20260308_200823_636718328.jsonl.meta.json +0 -1
  156. package/.beads/.br_history/issues.20260308_200827_033159453.jsonl +0 -21
  157. package/.beads/.br_history/issues.20260308_200827_033159453.jsonl.meta.json +0 -1
  158. package/.beads/.br_history/issues.20260308_200829_595900053.jsonl +0 -22
  159. package/.beads/.br_history/issues.20260308_200829_595900053.jsonl.meta.json +0 -1
  160. package/.beads/.br_history/issues.20260308_200834_079930100.jsonl +0 -23
  161. package/.beads/.br_history/issues.20260308_200834_079930100.jsonl.meta.json +0 -1
  162. package/.beads/.br_history/issues.20260308_200858_370924996.jsonl +0 -24
  163. package/.beads/.br_history/issues.20260308_200858_370924996.jsonl.meta.json +0 -1
  164. package/.beads/.br_history/issues.20260308_201031_019730855.jsonl +0 -24
  165. package/.beads/.br_history/issues.20260308_201031_019730855.jsonl.meta.json +0 -1
  166. package/.beads/.br_history/issues.20260308_201031_578974884.jsonl +0 -24
  167. package/.beads/.br_history/issues.20260308_201031_578974884.jsonl.meta.json +0 -1
  168. package/.beads/.br_history/issues.20260308_201054_780345548.jsonl +0 -24
  169. package/.beads/.br_history/issues.20260308_201054_780345548.jsonl.meta.json +0 -1
  170. package/.beads/.br_history/issues.20260308_201054_896980019.jsonl +0 -24
  171. package/.beads/.br_history/issues.20260308_201054_896980019.jsonl.meta.json +0 -1
  172. package/.beads/.br_history/issues.20260308_201128_599819688.jsonl +0 -24
  173. package/.beads/.br_history/issues.20260308_201128_599819688.jsonl.meta.json +0 -1
  174. package/.beads/.br_history/issues.20260308_201128_710221699.jsonl +0 -24
  175. package/.beads/.br_history/issues.20260308_201128_710221699.jsonl.meta.json +0 -1
  176. package/.beads/.br_history/issues.20260308_201204_745649213.jsonl +0 -24
  177. package/.beads/.br_history/issues.20260308_201204_745649213.jsonl.meta.json +0 -1
  178. package/.beads/.br_history/issues.20260308_201338_908436144.jsonl +0 -24
  179. package/.beads/.br_history/issues.20260308_201338_908436144.jsonl.meta.json +0 -1
  180. package/.beads/.br_history/issues.20260308_201344_734860714.jsonl +0 -25
  181. package/.beads/.br_history/issues.20260308_201344_734860714.jsonl.meta.json +0 -1
  182. package/.beads/.br_history/issues.20260308_201630_819282295.jsonl +0 -25
  183. package/.beads/.br_history/issues.20260308_201630_819282295.jsonl.meta.json +0 -1
  184. package/.beads/.br_history/issues.20260308_203538_054279699.jsonl +0 -25
  185. package/.beads/.br_history/issues.20260308_203538_054279699.jsonl.meta.json +0 -1
  186. package/.beads/.br_history/issues.20260308_203547_597113070.jsonl +0 -26
  187. package/.beads/.br_history/issues.20260308_203547_597113070.jsonl.meta.json +0 -1
  188. package/.beads/.br_history/issues.20260308_203547_775139216.jsonl +0 -27
  189. package/.beads/.br_history/issues.20260308_203547_775139216.jsonl.meta.json +0 -1
  190. package/.beads/.br_history/issues.20260308_203547_950724773.jsonl +0 -28
  191. package/.beads/.br_history/issues.20260308_203547_950724773.jsonl.meta.json +0 -1
  192. package/.beads/.br_history/issues.20260308_203548_107684523.jsonl +0 -29
  193. package/.beads/.br_history/issues.20260308_203548_107684523.jsonl.meta.json +0 -1
  194. package/.beads/.br_history/issues.20260308_203548_310389993.jsonl +0 -30
  195. package/.beads/.br_history/issues.20260308_203548_310389993.jsonl.meta.json +0 -1
  196. package/.beads/.br_history/issues.20260308_203825_953337320.jsonl +0 -31
  197. package/.beads/.br_history/issues.20260308_203825_953337320.jsonl.meta.json +0 -1
  198. package/.beads/.br_history/issues.20260308_204056_071377736.jsonl +0 -32
  199. package/.beads/.br_history/issues.20260308_204056_071377736.jsonl.meta.json +0 -1
  200. package/.beads/.br_history/issues.20260308_205141_517616844.jsonl +0 -32
  201. package/.beads/.br_history/issues.20260308_205141_517616844.jsonl.meta.json +0 -1
  202. package/.beads/.br_history/issues.20260308_205141_648994024.jsonl +0 -32
  203. package/.beads/.br_history/issues.20260308_205141_648994024.jsonl.meta.json +0 -1
  204. package/.beads/.br_history/issues.20260308_205141_867598036.jsonl +0 -32
  205. package/.beads/.br_history/issues.20260308_205141_867598036.jsonl.meta.json +0 -1
  206. package/.beads/.br_history/issues.20260308_205142_094157355.jsonl +0 -32
  207. package/.beads/.br_history/issues.20260308_205142_094157355.jsonl.meta.json +0 -1
  208. package/.beads/.br_history/issues.20260308_205142_327315677.jsonl +0 -32
  209. package/.beads/.br_history/issues.20260308_205142_327315677.jsonl.meta.json +0 -1
  210. package/.beads/.br_history/issues.20260308_205142_545563822.jsonl +0 -32
  211. package/.beads/.br_history/issues.20260308_205142_545563822.jsonl.meta.json +0 -1
  212. package/.beads/.br_history/issues.20260308_205213_061989333.jsonl +0 -32
  213. package/.beads/.br_history/issues.20260308_205213_061989333.jsonl.meta.json +0 -1
  214. package/.beads/.br_history/issues.20260308_205213_181103364.jsonl +0 -32
  215. package/.beads/.br_history/issues.20260308_205213_181103364.jsonl.meta.json +0 -1
  216. package/.beads/.br_history/issues.20260308_205213_408872234.jsonl +0 -32
  217. package/.beads/.br_history/issues.20260308_205213_408872234.jsonl.meta.json +0 -1
  218. package/.beads/.br_history/issues.20260308_205213_616681652.jsonl +0 -32
  219. package/.beads/.br_history/issues.20260308_205213_616681652.jsonl.meta.json +0 -1
  220. package/.beads/.br_history/issues.20260308_205213_821507069.jsonl +0 -32
  221. package/.beads/.br_history/issues.20260308_205213_821507069.jsonl.meta.json +0 -1
  222. package/.beads/.br_history/issues.20260308_205214_026661112.jsonl +0 -32
  223. package/.beads/.br_history/issues.20260308_205214_026661112.jsonl.meta.json +0 -1
  224. package/.beads/.br_history/issues.20260308_205454_955250554.jsonl +0 -32
  225. package/.beads/.br_history/issues.20260308_205454_955250554.jsonl.meta.json +0 -1
  226. package/.beads/.br_history/issues.20260308_205556_337800392.jsonl +0 -33
  227. package/.beads/.br_history/issues.20260308_205556_337800392.jsonl.meta.json +0 -1
  228. package/.beads/.br_history/issues.20260308_205824_274686694.jsonl +0 -33
  229. package/.beads/.br_history/issues.20260308_205824_274686694.jsonl.meta.json +0 -1
  230. package/.beads/.br_history/issues.20260308_210240_583768328.jsonl +0 -34
  231. package/.beads/.br_history/issues.20260308_210240_583768328.jsonl.meta.json +0 -1
  232. package/.beads/.br_history/issues.20260308_212223_641541494.jsonl +0 -34
  233. package/.beads/.br_history/issues.20260308_212223_641541494.jsonl.meta.json +0 -1
  234. package/.beads/.br_history/issues.20260308_212227_735550996.jsonl +0 -35
  235. package/.beads/.br_history/issues.20260308_212227_735550996.jsonl.meta.json +0 -1
  236. package/.beads/.br_history/issues.20260308_212232_547298548.jsonl +0 -36
  237. package/.beads/.br_history/issues.20260308_212232_547298548.jsonl.meta.json +0 -1
  238. package/.beads/.br_history/issues.20260308_212528_843628125.jsonl +0 -37
  239. package/.beads/.br_history/issues.20260308_212528_843628125.jsonl.meta.json +0 -1
  240. package/.beads/.br_history/issues.20260308_212529_094530502.jsonl +0 -38
  241. package/.beads/.br_history/issues.20260308_212529_094530502.jsonl.meta.json +0 -1
  242. package/.beads/.br_history/issues.20260308_212529_331000853.jsonl +0 -39
  243. package/.beads/.br_history/issues.20260308_212529_331000853.jsonl.meta.json +0 -1
  244. package/.beads/.br_history/issues.20260308_212529_587925652.jsonl +0 -40
  245. package/.beads/.br_history/issues.20260308_212529_587925652.jsonl.meta.json +0 -1
  246. package/.beads/.br_history/issues.20260308_212804_927764103.jsonl +0 -41
  247. package/.beads/.br_history/issues.20260308_212804_927764103.jsonl.meta.json +0 -1
  248. package/.beads/.br_history/issues.20260308_212805_153673453.jsonl +0 -42
  249. package/.beads/.br_history/issues.20260308_212805_153673453.jsonl.meta.json +0 -1
  250. package/.beads/.br_history/issues.20260308_212805_415982363.jsonl +0 -43
  251. package/.beads/.br_history/issues.20260308_212805_415982363.jsonl.meta.json +0 -1
  252. package/.beads/.br_history/issues.20260308_212805_657497741.jsonl +0 -44
  253. package/.beads/.br_history/issues.20260308_212805_657497741.jsonl.meta.json +0 -1
  254. package/.beads/.br_history/issues.20260308_212805_952838724.jsonl +0 -45
  255. package/.beads/.br_history/issues.20260308_212805_952838724.jsonl.meta.json +0 -1
  256. package/.beads/.br_history/issues.20260308_212806_325433779.jsonl +0 -46
  257. package/.beads/.br_history/issues.20260308_212806_325433779.jsonl.meta.json +0 -1
  258. package/.beads/.br_history/issues.20260308_212806_584685598.jsonl +0 -47
  259. package/.beads/.br_history/issues.20260308_212806_584685598.jsonl.meta.json +0 -1
  260. package/.beads/.br_history/issues.20260308_212806_827817208.jsonl +0 -48
  261. package/.beads/.br_history/issues.20260308_212806_827817208.jsonl.meta.json +0 -1
  262. package/.beads/.br_history/issues.20260308_212807_111320451.jsonl +0 -49
  263. package/.beads/.br_history/issues.20260308_212807_111320451.jsonl.meta.json +0 -1
  264. package/.beads/.br_history/issues.20260308_212807_409545536.jsonl +0 -50
  265. package/.beads/.br_history/issues.20260308_212807_409545536.jsonl.meta.json +0 -1
  266. package/.beads/.br_history/issues.20260308_212807_625063294.jsonl +0 -51
  267. package/.beads/.br_history/issues.20260308_212807_625063294.jsonl.meta.json +0 -1
  268. package/.beads/.br_history/issues.20260308_212807_843906551.jsonl +0 -52
  269. package/.beads/.br_history/issues.20260308_212807_843906551.jsonl.meta.json +0 -1
  270. package/.beads/.br_history/issues.20260308_212808_100304073.jsonl +0 -53
  271. package/.beads/.br_history/issues.20260308_212808_100304073.jsonl.meta.json +0 -1
  272. package/.beads/.br_history/issues.20260308_212808_324723976.jsonl +0 -54
  273. package/.beads/.br_history/issues.20260308_212808_324723976.jsonl.meta.json +0 -1
  274. package/.beads/.br_history/issues.20260308_212808_557513104.jsonl +0 -55
  275. package/.beads/.br_history/issues.20260308_212808_557513104.jsonl.meta.json +0 -1
  276. package/.beads/.br_history/issues.20260308_212808_788048322.jsonl +0 -56
  277. package/.beads/.br_history/issues.20260308_212808_788048322.jsonl.meta.json +0 -1
  278. package/.beads/.br_history/issues.20260308_213702_613249728.jsonl +0 -57
  279. package/.beads/.br_history/issues.20260308_213702_613249728.jsonl.meta.json +0 -1
  280. package/.beads/.br_history/issues.20260308_213715_115792063.jsonl +0 -57
  281. package/.beads/.br_history/issues.20260308_213715_115792063.jsonl.meta.json +0 -1
  282. package/.beads/.br_history/issues.20260308_213715_462220666.jsonl +0 -57
  283. package/.beads/.br_history/issues.20260308_213715_462220666.jsonl.meta.json +0 -1
  284. package/.beads/.br_history/issues.20260308_213727_191258923.jsonl +0 -57
  285. package/.beads/.br_history/issues.20260308_213727_191258923.jsonl.meta.json +0 -1
  286. package/.beads/.br_history/issues.20260308_213727_684383652.jsonl +0 -57
  287. package/.beads/.br_history/issues.20260308_213727_684383652.jsonl.meta.json +0 -1
  288. package/.beads/.br_history/issues.20260308_213735_751882991.jsonl +0 -57
  289. package/.beads/.br_history/issues.20260308_213735_751882991.jsonl.meta.json +0 -1
  290. package/.beads/.br_history/issues.20260308_222052_279844960.jsonl +0 -57
  291. package/.beads/.br_history/issues.20260308_222052_279844960.jsonl.meta.json +0 -1
  292. package/.beads/.br_history/issues.20260308_222056_873282114.jsonl +0 -57
  293. package/.beads/.br_history/issues.20260308_222056_873282114.jsonl.meta.json +0 -1
  294. package/.beads/.br_history/issues.20260308_222103_402410761.jsonl +0 -57
  295. package/.beads/.br_history/issues.20260308_222103_402410761.jsonl.meta.json +0 -1
  296. package/.beads/.br_history/issues.20260308_235202_180577215.jsonl +0 -57
  297. package/.beads/.br_history/issues.20260308_235202_180577215.jsonl.meta.json +0 -1
  298. package/.beads/.br_history/issues.20260308_235202_387414163.jsonl +0 -57
  299. package/.beads/.br_history/issues.20260308_235202_387414163.jsonl.meta.json +0 -1
  300. package/.beads/.br_history/issues.20260308_235202_564422794.jsonl +0 -57
  301. package/.beads/.br_history/issues.20260308_235202_564422794.jsonl.meta.json +0 -1
  302. package/.beads/.br_history/issues.20260308_235202_742600597.jsonl +0 -57
  303. package/.beads/.br_history/issues.20260308_235202_742600597.jsonl.meta.json +0 -1
  304. package/.beads/.br_history/issues.20260308_235208_133360069.jsonl +0 -57
  305. package/.beads/.br_history/issues.20260308_235208_133360069.jsonl.meta.json +0 -1
  306. package/.beads/.br_history/issues.20260308_235505_473406307.jsonl +0 -57
  307. package/.beads/.br_history/issues.20260308_235505_473406307.jsonl.meta.json +0 -1
  308. package/.beads/.br_history/issues.20260308_235505_662360489.jsonl +0 -57
  309. package/.beads/.br_history/issues.20260308_235505_662360489.jsonl.meta.json +0 -1
  310. package/.beads/.br_history/issues.20260308_235505_843935624.jsonl +0 -57
  311. package/.beads/.br_history/issues.20260308_235505_843935624.jsonl.meta.json +0 -1
  312. package/.beads/.br_history/issues.20260308_235506_044530221.jsonl +0 -57
  313. package/.beads/.br_history/issues.20260308_235506_044530221.jsonl.meta.json +0 -1
  314. package/.beads/.br_history/issues.20260309_002618_115728731.jsonl +0 -57
  315. package/.beads/.br_history/issues.20260309_002618_115728731.jsonl.meta.json +0 -1
  316. package/.beads/.br_history/issues.20260309_003748_878174586.jsonl +0 -57
  317. package/.beads/.br_history/issues.20260309_003748_878174586.jsonl.meta.json +0 -1
  318. package/.beads/.br_history/issues.20260309_004057_868755623.jsonl +0 -57
  319. package/.beads/.br_history/issues.20260309_004057_868755623.jsonl.meta.json +0 -1
  320. package/.beads/.br_history/issues.20260309_004058_512842163.jsonl +0 -57
  321. package/.beads/.br_history/issues.20260309_004058_512842163.jsonl.meta.json +0 -1
  322. package/.beads/.br_history/issues.20260309_004058_994445226.jsonl +0 -57
  323. package/.beads/.br_history/issues.20260309_004058_994445226.jsonl.meta.json +0 -1
  324. package/.beads/.br_history/issues.20260309_004059_475988596.jsonl +0 -57
  325. package/.beads/.br_history/issues.20260309_004059_475988596.jsonl.meta.json +0 -1
  326. package/.beads/.br_history/issues.20260309_161902_566857851.jsonl +0 -57
  327. package/.beads/.br_history/issues.20260309_161902_566857851.jsonl.meta.json +0 -1
  328. package/.beads/.br_history/issues.20260309_170512_277017739.jsonl +0 -57
  329. package/.beads/.br_history/issues.20260309_170512_277017739.jsonl.meta.json +0 -1
  330. package/.beads/.br_history/issues.20260309_170512_477876921.jsonl +0 -57
  331. package/.beads/.br_history/issues.20260309_170512_477876921.jsonl.meta.json +0 -1
  332. package/.beads/.br_history/issues.20260309_170512_664382701.jsonl +0 -57
  333. package/.beads/.br_history/issues.20260309_170512_664382701.jsonl.meta.json +0 -1
  334. package/.beads/.br_history/issues.20260309_170512_859400333.jsonl +0 -57
  335. package/.beads/.br_history/issues.20260309_170512_859400333.jsonl.meta.json +0 -1
  336. package/.beads/.br_history/issues.20260309_212326_082771164.jsonl +0 -57
  337. package/.beads/.br_history/issues.20260309_212326_082771164.jsonl.meta.json +0 -1
  338. package/.beads/.br_history/issues.20260309_212326_245619716.jsonl +0 -58
  339. package/.beads/.br_history/issues.20260309_212326_245619716.jsonl.meta.json +0 -1
  340. package/.beads/.br_history/issues.20260309_212326_403198317.jsonl +0 -59
  341. package/.beads/.br_history/issues.20260309_212326_403198317.jsonl.meta.json +0 -1
  342. package/.beads/.br_history/issues.20260309_212332_539197678.jsonl +0 -60
  343. package/.beads/.br_history/issues.20260309_212332_539197678.jsonl.meta.json +0 -1
  344. package/.beads/.br_history/issues.20260309_212332_731373599.jsonl +0 -60
  345. package/.beads/.br_history/issues.20260309_212332_731373599.jsonl.meta.json +0 -1
  346. package/.beads/.br_history/issues.20260309_212332_928710953.jsonl +0 -60
  347. package/.beads/.br_history/issues.20260309_212332_928710953.jsonl.meta.json +0 -1
  348. package/.beads/.br_history/issues.20260309_213021_341505240.jsonl +0 -60
  349. package/.beads/.br_history/issues.20260309_213021_341505240.jsonl.meta.json +0 -1
  350. package/.beads/.br_history/issues.20260309_213022_023136934.jsonl +0 -60
  351. package/.beads/.br_history/issues.20260309_213022_023136934.jsonl.meta.json +0 -1
  352. package/.beads/.br_history/issues.20260309_213022_400050719.jsonl +0 -60
  353. package/.beads/.br_history/issues.20260309_213022_400050719.jsonl.meta.json +0 -1
  354. package/.beads/config.yaml +0 -4
  355. package/.beads/issues.jsonl +0 -60
  356. package/.beads/metadata.json +0 -4
  357. package/docs/mcp-cheatsheet.md +0 -324
  358. package/docs/visual-overview.md +0 -21
  359. package/ref-monty/.cargo/config.toml +0 -3
  360. package/ref-monty/.claude/settings.json +0 -60
  361. package/ref-monty/.claude/skills/fastmod/SKILL.md +0 -22
  362. package/ref-monty/.claude/skills/python-playground/SKILL.md +0 -47
  363. package/ref-monty/.codecov.yml +0 -12
  364. package/ref-monty/.github/actions/build-pgo-wheel/action.yml +0 -72
  365. package/ref-monty/.github/workflows/ci.yml +0 -776
  366. package/ref-monty/.github/workflows/codspeed.yml +0 -45
  367. package/ref-monty/.github/workflows/init-npm-packages.yml +0 -82
  368. package/ref-monty/.pre-commit-config.yaml +0 -47
  369. package/ref-monty/.python-version +0 -1
  370. package/ref-monty/.rustfmt.toml +0 -4
  371. package/ref-monty/.zed/settings.json +0 -11
  372. package/ref-monty/CLAUDE.md +0 -535
  373. package/ref-monty/Cargo.lock +0 -3798
  374. package/ref-monty/Cargo.toml +0 -87
  375. package/ref-monty/LICENSE +0 -21
  376. package/ref-monty/Makefile +0 -216
  377. package/ref-monty/README.md +0 -430
  378. package/ref-monty/RELEASING.md +0 -47
  379. package/ref-monty/crates/fuzz/Cargo.toml +0 -30
  380. package/ref-monty/crates/fuzz/fuzz_targets/string_input_panic.rs +0 -37
  381. package/ref-monty/crates/fuzz/fuzz_targets/tokens_input_panic.rs +0 -552
  382. package/ref-monty/crates/monty/Cargo.toml +0 -68
  383. package/ref-monty/crates/monty/benches/main.rs +0 -247
  384. package/ref-monty/crates/monty/build.rs +0 -10
  385. package/ref-monty/crates/monty/src/args.rs +0 -733
  386. package/ref-monty/crates/monty/src/asyncio.rs +0 -179
  387. package/ref-monty/crates/monty/src/builtins/abs.rs +0 -55
  388. package/ref-monty/crates/monty/src/builtins/all.rs +0 -30
  389. package/ref-monty/crates/monty/src/builtins/any.rs +0 -30
  390. package/ref-monty/crates/monty/src/builtins/bin.rs +0 -59
  391. package/ref-monty/crates/monty/src/builtins/chr.rs +0 -46
  392. package/ref-monty/crates/monty/src/builtins/divmod.rs +0 -164
  393. package/ref-monty/crates/monty/src/builtins/enumerate.rs +0 -52
  394. package/ref-monty/crates/monty/src/builtins/filter.rs +0 -67
  395. package/ref-monty/crates/monty/src/builtins/getattr.rs +0 -65
  396. package/ref-monty/crates/monty/src/builtins/hash.rs +0 -28
  397. package/ref-monty/crates/monty/src/builtins/hex.rs +0 -58
  398. package/ref-monty/crates/monty/src/builtins/id.rs +0 -24
  399. package/ref-monty/crates/monty/src/builtins/isinstance.rs +0 -68
  400. package/ref-monty/crates/monty/src/builtins/len.rs +0 -25
  401. package/ref-monty/crates/monty/src/builtins/map.rs +0 -98
  402. package/ref-monty/crates/monty/src/builtins/min_max.rs +0 -113
  403. package/ref-monty/crates/monty/src/builtins/mod.rs +0 -246
  404. package/ref-monty/crates/monty/src/builtins/next.rs +0 -21
  405. package/ref-monty/crates/monty/src/builtins/oct.rs +0 -59
  406. package/ref-monty/crates/monty/src/builtins/ord.rs +0 -67
  407. package/ref-monty/crates/monty/src/builtins/pow.rs +0 -365
  408. package/ref-monty/crates/monty/src/builtins/print.rs +0 -141
  409. package/ref-monty/crates/monty/src/builtins/repr.rs +0 -16
  410. package/ref-monty/crates/monty/src/builtins/reversed.rs +0 -28
  411. package/ref-monty/crates/monty/src/builtins/round.rs +0 -174
  412. package/ref-monty/crates/monty/src/builtins/sorted.rs +0 -151
  413. package/ref-monty/crates/monty/src/builtins/sum.rs +0 -66
  414. package/ref-monty/crates/monty/src/builtins/type_.rs +0 -16
  415. package/ref-monty/crates/monty/src/builtins/zip.rs +0 -77
  416. package/ref-monty/crates/monty/src/bytecode/builder.rs +0 -699
  417. package/ref-monty/crates/monty/src/bytecode/code.rs +0 -310
  418. package/ref-monty/crates/monty/src/bytecode/compiler.rs +0 -3206
  419. package/ref-monty/crates/monty/src/bytecode/mod.rs +0 -24
  420. package/ref-monty/crates/monty/src/bytecode/op.rs +0 -617
  421. package/ref-monty/crates/monty/src/bytecode/vm/async_exec.rs +0 -1058
  422. package/ref-monty/crates/monty/src/bytecode/vm/attr.rs +0 -63
  423. package/ref-monty/crates/monty/src/bytecode/vm/binary.rs +0 -487
  424. package/ref-monty/crates/monty/src/bytecode/vm/call.rs +0 -767
  425. package/ref-monty/crates/monty/src/bytecode/vm/collections.rs +0 -741
  426. package/ref-monty/crates/monty/src/bytecode/vm/compare.rs +0 -147
  427. package/ref-monty/crates/monty/src/bytecode/vm/exceptions.rs +0 -297
  428. package/ref-monty/crates/monty/src/bytecode/vm/format.rs +0 -132
  429. package/ref-monty/crates/monty/src/bytecode/vm/mod.rs +0 -1958
  430. package/ref-monty/crates/monty/src/bytecode/vm/scheduler.rs +0 -620
  431. package/ref-monty/crates/monty/src/exception_private.rs +0 -1513
  432. package/ref-monty/crates/monty/src/exception_public.rs +0 -346
  433. package/ref-monty/crates/monty/src/expressions.rs +0 -694
  434. package/ref-monty/crates/monty/src/fstring.rs +0 -854
  435. package/ref-monty/crates/monty/src/function.rs +0 -119
  436. package/ref-monty/crates/monty/src/heap.rs +0 -1073
  437. package/ref-monty/crates/monty/src/heap_data.rs +0 -985
  438. package/ref-monty/crates/monty/src/heap_traits.rs +0 -312
  439. package/ref-monty/crates/monty/src/intern.rs +0 -837
  440. package/ref-monty/crates/monty/src/io.rs +0 -106
  441. package/ref-monty/crates/monty/src/lib.rs +0 -52
  442. package/ref-monty/crates/monty/src/modules/asyncio.rs +0 -144
  443. package/ref-monty/crates/monty/src/modules/math.rs +0 -1453
  444. package/ref-monty/crates/monty/src/modules/mod.rs +0 -120
  445. package/ref-monty/crates/monty/src/modules/os.rs +0 -116
  446. package/ref-monty/crates/monty/src/modules/pathlib.rs +0 -33
  447. package/ref-monty/crates/monty/src/modules/re.rs +0 -606
  448. package/ref-monty/crates/monty/src/modules/sys.rs +0 -60
  449. package/ref-monty/crates/monty/src/modules/typing.rs +0 -70
  450. package/ref-monty/crates/monty/src/namespace.rs +0 -21
  451. package/ref-monty/crates/monty/src/object.rs +0 -1040
  452. package/ref-monty/crates/monty/src/os.rs +0 -215
  453. package/ref-monty/crates/monty/src/parse.rs +0 -1730
  454. package/ref-monty/crates/monty/src/prepare.rs +0 -3015
  455. package/ref-monty/crates/monty/src/repl.rs +0 -1109
  456. package/ref-monty/crates/monty/src/resource.rs +0 -559
  457. package/ref-monty/crates/monty/src/run.rs +0 -457
  458. package/ref-monty/crates/monty/src/run_progress.rs +0 -821
  459. package/ref-monty/crates/monty/src/signature.rs +0 -651
  460. package/ref-monty/crates/monty/src/sorting.rs +0 -100
  461. package/ref-monty/crates/monty/src/types/bytes.rs +0 -2356
  462. package/ref-monty/crates/monty/src/types/dataclass.rs +0 -345
  463. package/ref-monty/crates/monty/src/types/dict.rs +0 -879
  464. package/ref-monty/crates/monty/src/types/dict_view.rs +0 -619
  465. package/ref-monty/crates/monty/src/types/iter.rs +0 -799
  466. package/ref-monty/crates/monty/src/types/list.rs +0 -929
  467. package/ref-monty/crates/monty/src/types/long_int.rs +0 -211
  468. package/ref-monty/crates/monty/src/types/mod.rs +0 -48
  469. package/ref-monty/crates/monty/src/types/module.rs +0 -146
  470. package/ref-monty/crates/monty/src/types/namedtuple.rs +0 -261
  471. package/ref-monty/crates/monty/src/types/path.rs +0 -596
  472. package/ref-monty/crates/monty/src/types/property.rs +0 -35
  473. package/ref-monty/crates/monty/src/types/py_trait.rs +0 -322
  474. package/ref-monty/crates/monty/src/types/range.rs +0 -285
  475. package/ref-monty/crates/monty/src/types/re_match.rs +0 -522
  476. package/ref-monty/crates/monty/src/types/re_pattern.rs +0 -726
  477. package/ref-monty/crates/monty/src/types/set.rs +0 -1373
  478. package/ref-monty/crates/monty/src/types/slice.rs +0 -257
  479. package/ref-monty/crates/monty/src/types/str.rs +0 -2051
  480. package/ref-monty/crates/monty/src/types/tuple.rs +0 -376
  481. package/ref-monty/crates/monty/src/types/type.rs +0 -407
  482. package/ref-monty/crates/monty/src/value.rs +0 -2558
  483. package/ref-monty/crates/monty/test_cases/args__dict_get_no_args.py +0 -3
  484. package/ref-monty/crates/monty/test_cases/args__dict_get_too_many.py +0 -3
  485. package/ref-monty/crates/monty/test_cases/args__dict_items_with_args.py +0 -3
  486. package/ref-monty/crates/monty/test_cases/args__dict_keys_with_args.py +0 -3
  487. package/ref-monty/crates/monty/test_cases/args__dict_pop_no_args.py +0 -3
  488. package/ref-monty/crates/monty/test_cases/args__dict_pop_too_many.py +0 -3
  489. package/ref-monty/crates/monty/test_cases/args__dict_values_with_args.py +0 -3
  490. package/ref-monty/crates/monty/test_cases/args__id_too_many.py +0 -2
  491. package/ref-monty/crates/monty/test_cases/args__len_no_args.py +0 -2
  492. package/ref-monty/crates/monty/test_cases/args__len_too_many.py +0 -2
  493. package/ref-monty/crates/monty/test_cases/args__len_type_error_int.py +0 -9
  494. package/ref-monty/crates/monty/test_cases/args__len_type_error_none.py +0 -9
  495. package/ref-monty/crates/monty/test_cases/args__list_append_no_args.py +0 -3
  496. package/ref-monty/crates/monty/test_cases/args__list_append_too_many.py +0 -3
  497. package/ref-monty/crates/monty/test_cases/args__list_insert_too_few.py +0 -3
  498. package/ref-monty/crates/monty/test_cases/args__list_insert_too_many.py +0 -3
  499. package/ref-monty/crates/monty/test_cases/args__repr_no_args.py +0 -2
  500. package/ref-monty/crates/monty/test_cases/arith__div_zero_float.py +0 -2
  501. package/ref-monty/crates/monty/test_cases/arith__div_zero_int.py +0 -2
  502. package/ref-monty/crates/monty/test_cases/arith__floordiv_zero_float.py +0 -2
  503. package/ref-monty/crates/monty/test_cases/arith__floordiv_zero_int.py +0 -2
  504. package/ref-monty/crates/monty/test_cases/arith__pow_zero_neg.py +0 -2
  505. package/ref-monty/crates/monty/test_cases/arith__pow_zero_neg_builtin.py +0 -9
  506. package/ref-monty/crates/monty/test_cases/assert__expr_fail.py +0 -2
  507. package/ref-monty/crates/monty/test_cases/assert__fail.py +0 -2
  508. package/ref-monty/crates/monty/test_cases/assert__fail_msg.py +0 -2
  509. package/ref-monty/crates/monty/test_cases/assert__fn_fail.py +0 -3
  510. package/ref-monty/crates/monty/test_cases/assert__ops.py +0 -11
  511. package/ref-monty/crates/monty/test_cases/async__asyncio_run.py +0 -47
  512. package/ref-monty/crates/monty/test_cases/async__basic.py +0 -10
  513. package/ref-monty/crates/monty/test_cases/async__closure.py +0 -14
  514. package/ref-monty/crates/monty/test_cases/async__double_await_coroutine.py +0 -16
  515. package/ref-monty/crates/monty/test_cases/async__exception.py +0 -10
  516. package/ref-monty/crates/monty/test_cases/async__ext_call.py +0 -73
  517. package/ref-monty/crates/monty/test_cases/async__gather_all.py +0 -85
  518. package/ref-monty/crates/monty/test_cases/async__nested_await.py +0 -15
  519. package/ref-monty/crates/monty/test_cases/async__nested_gather_ext.py +0 -37
  520. package/ref-monty/crates/monty/test_cases/async__not_awaitable.py +0 -10
  521. package/ref-monty/crates/monty/test_cases/async__not_imported.py +0 -14
  522. package/ref-monty/crates/monty/test_cases/async__recursion_depth_isolation.py +0 -27
  523. package/ref-monty/crates/monty/test_cases/async__return_types.py +0 -31
  524. package/ref-monty/crates/monty/test_cases/async__sequential.py +0 -16
  525. package/ref-monty/crates/monty/test_cases/async__traceback.py +0 -19
  526. package/ref-monty/crates/monty/test_cases/async__with_args.py +0 -14
  527. package/ref-monty/crates/monty/test_cases/attr__get_int_error.py +0 -9
  528. package/ref-monty/crates/monty/test_cases/attr__get_list_error.py +0 -9
  529. package/ref-monty/crates/monty/test_cases/attr__set_frozen_nonfield.py +0 -12
  530. package/ref-monty/crates/monty/test_cases/attr__set_int_error.py +0 -10
  531. package/ref-monty/crates/monty/test_cases/attr__set_list_error.py +0 -10
  532. package/ref-monty/crates/monty/test_cases/bench__kitchen_sink.py +0 -68
  533. package/ref-monty/crates/monty/test_cases/bool__ops.py +0 -20
  534. package/ref-monty/crates/monty/test_cases/builtin__add_type_error.py +0 -2
  535. package/ref-monty/crates/monty/test_cases/builtin__filter.py +0 -62
  536. package/ref-monty/crates/monty/test_cases/builtin__filter_not_iterable.py +0 -11
  537. package/ref-monty/crates/monty/test_cases/builtin__getattr.py +0 -84
  538. package/ref-monty/crates/monty/test_cases/builtin__iter_funcs.py +0 -42
  539. package/ref-monty/crates/monty/test_cases/builtin__iter_next.py +0 -66
  540. package/ref-monty/crates/monty/test_cases/builtin__map.py +0 -74
  541. package/ref-monty/crates/monty/test_cases/builtin__map_not_iterable.py +0 -11
  542. package/ref-monty/crates/monty/test_cases/builtin__math_funcs.py +0 -154
  543. package/ref-monty/crates/monty/test_cases/builtin__more_iter_funcs.py +0 -148
  544. package/ref-monty/crates/monty/test_cases/builtin__next_stop_iteration.py +0 -10
  545. package/ref-monty/crates/monty/test_cases/builtin__print_invalid_kwarg.py +0 -9
  546. package/ref-monty/crates/monty/test_cases/builtin__print_kwargs.py +0 -12
  547. package/ref-monty/crates/monty/test_cases/builtin__repr.py +0 -3
  548. package/ref-monty/crates/monty/test_cases/builtin__string_funcs.py +0 -73
  549. package/ref-monty/crates/monty/test_cases/bytes__decode_invalid_utf8.py +0 -18
  550. package/ref-monty/crates/monty/test_cases/bytes__endswith_str_error.py +0 -10
  551. package/ref-monty/crates/monty/test_cases/bytes__getitem_index_error.py +0 -10
  552. package/ref-monty/crates/monty/test_cases/bytes__index_start_gt_end.py +0 -10
  553. package/ref-monty/crates/monty/test_cases/bytes__methods.py +0 -394
  554. package/ref-monty/crates/monty/test_cases/bytes__negative_count.py +0 -9
  555. package/ref-monty/crates/monty/test_cases/bytes__ops.py +0 -90
  556. package/ref-monty/crates/monty/test_cases/bytes__startswith_str_error.py +0 -10
  557. package/ref-monty/crates/monty/test_cases/call_object.py +0 -3
  558. package/ref-monty/crates/monty/test_cases/chain_comparison__all.py +0 -79
  559. package/ref-monty/crates/monty/test_cases/closure__param_shadows_outer.py +0 -81
  560. package/ref-monty/crates/monty/test_cases/closure__pep448.py +0 -203
  561. package/ref-monty/crates/monty/test_cases/closure__undefined_nonlocal.py +0 -13
  562. package/ref-monty/crates/monty/test_cases/compare__mixed_types.py +0 -120
  563. package/ref-monty/crates/monty/test_cases/comprehension__all.py +0 -208
  564. package/ref-monty/crates/monty/test_cases/comprehension__scope.py +0 -7
  565. package/ref-monty/crates/monty/test_cases/comprehension__unbound_local.py +0 -14
  566. package/ref-monty/crates/monty/test_cases/dataclass__basic.py +0 -238
  567. package/ref-monty/crates/monty/test_cases/dataclass__call_field_error.py +0 -12
  568. package/ref-monty/crates/monty/test_cases/dataclass__frozen_set_error.py +0 -12
  569. package/ref-monty/crates/monty/test_cases/dataclass__get_missing_attr_error.py +0 -11
  570. package/ref-monty/crates/monty/test_cases/dict__get_unhashable_key.py +0 -3
  571. package/ref-monty/crates/monty/test_cases/dict__literal_unhashable_key.py +0 -2
  572. package/ref-monty/crates/monty/test_cases/dict__method_pop_missing_error.py +0 -3
  573. package/ref-monty/crates/monty/test_cases/dict__methods.py +0 -151
  574. package/ref-monty/crates/monty/test_cases/dict__ops.py +0 -133
  575. package/ref-monty/crates/monty/test_cases/dict__pop_unhashable_key.py +0 -4
  576. package/ref-monty/crates/monty/test_cases/dict__popitem_empty.py +0 -9
  577. package/ref-monty/crates/monty/test_cases/dict__subscript_missing_key.py +0 -3
  578. package/ref-monty/crates/monty/test_cases/dict__unhashable_dict_key.py +0 -2
  579. package/ref-monty/crates/monty/test_cases/dict__unhashable_list_key.py +0 -2
  580. package/ref-monty/crates/monty/test_cases/dict__unpack_type_error.py +0 -2
  581. package/ref-monty/crates/monty/test_cases/dict__views.py +0 -165
  582. package/ref-monty/crates/monty/test_cases/edge__all.py +0 -26
  583. package/ref-monty/crates/monty/test_cases/edge__float_int_mod.py +0 -2
  584. package/ref-monty/crates/monty/test_cases/edge__int_float_mod.py +0 -2
  585. package/ref-monty/crates/monty/test_cases/exc__args.py +0 -16
  586. package/ref-monty/crates/monty/test_cases/exc__str.py +0 -15
  587. package/ref-monty/crates/monty/test_cases/execute_ok__all.py +0 -54
  588. package/ref-monty/crates/monty/test_cases/execute_raise__error_instance_str.py +0 -2
  589. package/ref-monty/crates/monty/test_cases/execute_raise__error_no_args.py +0 -2
  590. package/ref-monty/crates/monty/test_cases/execute_raise__error_string_arg.py +0 -2
  591. package/ref-monty/crates/monty/test_cases/execute_raise__error_string_arg_quotes.py +0 -2
  592. package/ref-monty/crates/monty/test_cases/execute_raise__error_type.py +0 -2
  593. package/ref-monty/crates/monty/test_cases/execute_raise__raise_instance_via_var.py +0 -4
  594. package/ref-monty/crates/monty/test_cases/execute_raise__raise_list.py +0 -2
  595. package/ref-monty/crates/monty/test_cases/execute_raise__raise_number.py +0 -2
  596. package/ref-monty/crates/monty/test_cases/execute_raise__raise_type_call_via_var.py +0 -4
  597. package/ref-monty/crates/monty/test_cases/execute_raise__raise_type_direct.py +0 -3
  598. package/ref-monty/crates/monty/test_cases/execute_raise__raise_type_via_var.py +0 -4
  599. package/ref-monty/crates/monty/test_cases/ext_call__arg_side_effect_bug.py +0 -22
  600. package/ref-monty/crates/monty/test_cases/ext_call__augmented.py +0 -17
  601. package/ref-monty/crates/monty/test_cases/ext_call__augmented_refcount_bug.py +0 -7
  602. package/ref-monty/crates/monty/test_cases/ext_call__bare_raise_after_resume.py +0 -34
  603. package/ref-monty/crates/monty/test_cases/ext_call__basic.py +0 -99
  604. package/ref-monty/crates/monty/test_cases/ext_call__boolean.py +0 -37
  605. package/ref-monty/crates/monty/test_cases/ext_call__boolean_side_effect_hang.py +0 -17
  606. package/ref-monty/crates/monty/test_cases/ext_call__closure_bug.py +0 -16
  607. package/ref-monty/crates/monty/test_cases/ext_call__comparison.py +0 -26
  608. package/ref-monty/crates/monty/test_cases/ext_call__deep_call_stack.py +0 -18
  609. package/ref-monty/crates/monty/test_cases/ext_call__elif.py +0 -171
  610. package/ref-monty/crates/monty/test_cases/ext_call__exc.py +0 -4
  611. package/ref-monty/crates/monty/test_cases/ext_call__exc_deep_stack.py +0 -39
  612. package/ref-monty/crates/monty/test_cases/ext_call__exc_in_function.py +0 -17
  613. package/ref-monty/crates/monty/test_cases/ext_call__exc_nested_functions.py +0 -31
  614. package/ref-monty/crates/monty/test_cases/ext_call__ext_exc.py +0 -171
  615. package/ref-monty/crates/monty/test_cases/ext_call__for.py +0 -114
  616. package/ref-monty/crates/monty/test_cases/ext_call__fstring.py +0 -12
  617. package/ref-monty/crates/monty/test_cases/ext_call__if.py +0 -135
  618. package/ref-monty/crates/monty/test_cases/ext_call__if_condition.py +0 -37
  619. package/ref-monty/crates/monty/test_cases/ext_call__in_closure.py +0 -14
  620. package/ref-monty/crates/monty/test_cases/ext_call__in_function.py +0 -40
  621. package/ref-monty/crates/monty/test_cases/ext_call__in_function_simple.py +0 -7
  622. package/ref-monty/crates/monty/test_cases/ext_call__literals.py +0 -17
  623. package/ref-monty/crates/monty/test_cases/ext_call__multi_in_func.py +0 -32
  624. package/ref-monty/crates/monty/test_cases/ext_call__name_lookup.py +0 -69
  625. package/ref-monty/crates/monty/test_cases/ext_call__name_lookup_undefined.py +0 -4
  626. package/ref-monty/crates/monty/test_cases/ext_call__nested_calls.py +0 -14
  627. package/ref-monty/crates/monty/test_cases/ext_call__recursion_bug.py +0 -19
  628. package/ref-monty/crates/monty/test_cases/ext_call__return.py +0 -28
  629. package/ref-monty/crates/monty/test_cases/ext_call__side_effects.py +0 -25
  630. package/ref-monty/crates/monty/test_cases/ext_call__subscript.py +0 -7
  631. package/ref-monty/crates/monty/test_cases/ext_call__ternary.py +0 -28
  632. package/ref-monty/crates/monty/test_cases/ext_call__try.py +0 -280
  633. package/ref-monty/crates/monty/test_cases/ext_call__try_simple.py +0 -10
  634. package/ref-monty/crates/monty/test_cases/ext_call__unary.py +0 -13
  635. package/ref-monty/crates/monty/test_cases/frozenset__ops.py +0 -178
  636. package/ref-monty/crates/monty/test_cases/fstring__all.py +0 -236
  637. package/ref-monty/crates/monty/test_cases/fstring__error_eq_align_on_str.py +0 -3
  638. package/ref-monty/crates/monty/test_cases/fstring__error_float_f_on_str.py +0 -3
  639. package/ref-monty/crates/monty/test_cases/fstring__error_int_d_on_float.py +0 -3
  640. package/ref-monty/crates/monty/test_cases/fstring__error_int_d_on_str.py +0 -3
  641. package/ref-monty/crates/monty/test_cases/fstring__error_invalid_spec.py +0 -4
  642. package/ref-monty/crates/monty/test_cases/fstring__error_invalid_spec_dynamic.py +0 -4
  643. package/ref-monty/crates/monty/test_cases/fstring__error_invalid_spec_str.py +0 -4
  644. package/ref-monty/crates/monty/test_cases/fstring__error_str_s_on_int.py +0 -3
  645. package/ref-monty/crates/monty/test_cases/function__call_duplicate_kwargs.py +0 -6
  646. package/ref-monty/crates/monty/test_cases/function__call_unpack.py +0 -42
  647. package/ref-monty/crates/monty/test_cases/function__defaults.py +0 -117
  648. package/ref-monty/crates/monty/test_cases/function__err_duplicate_arg.py +0 -7
  649. package/ref-monty/crates/monty/test_cases/function__err_duplicate_first_arg.py +0 -7
  650. package/ref-monty/crates/monty/test_cases/function__err_duplicate_kwarg_cleanup.py +0 -9
  651. package/ref-monty/crates/monty/test_cases/function__err_kwonly_as_positional.py +0 -7
  652. package/ref-monty/crates/monty/test_cases/function__err_missing_all_posonly.py +0 -7
  653. package/ref-monty/crates/monty/test_cases/function__err_missing_heap_cleanup.py +0 -9
  654. package/ref-monty/crates/monty/test_cases/function__err_missing_kwonly.py +0 -7
  655. package/ref-monty/crates/monty/test_cases/function__err_missing_posonly_with_kwarg.py +0 -7
  656. package/ref-monty/crates/monty/test_cases/function__err_missing_with_posonly.py +0 -7
  657. package/ref-monty/crates/monty/test_cases/function__err_posonly_as_kwarg.py +0 -7
  658. package/ref-monty/crates/monty/test_cases/function__err_posonly_first_as_kwarg.py +0 -7
  659. package/ref-monty/crates/monty/test_cases/function__err_too_many_posonly.py +0 -7
  660. package/ref-monty/crates/monty/test_cases/function__err_too_many_with_kwonly.py +0 -7
  661. package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg.py +0 -7
  662. package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg_cleanup.py +0 -9
  663. package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg_quote.py +0 -13
  664. package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg_simple.py +0 -7
  665. package/ref-monty/crates/monty/test_cases/function__err_unpack_duplicate_arg.py +0 -6
  666. package/ref-monty/crates/monty/test_cases/function__err_unpack_duplicate_heap.py +0 -8
  667. package/ref-monty/crates/monty/test_cases/function__err_unpack_int.py +0 -6
  668. package/ref-monty/crates/monty/test_cases/function__err_unpack_nonstring_key.py +0 -6
  669. package/ref-monty/crates/monty/test_cases/function__err_unpack_not_mapping.py +0 -6
  670. package/ref-monty/crates/monty/test_cases/function__kwargs_unpacking.py +0 -173
  671. package/ref-monty/crates/monty/test_cases/function__ops.py +0 -294
  672. package/ref-monty/crates/monty/test_cases/function__return_none.py +0 -42
  673. package/ref-monty/crates/monty/test_cases/function__signatures.py +0 -47
  674. package/ref-monty/crates/monty/test_cases/function__too_few_args_all.py +0 -6
  675. package/ref-monty/crates/monty/test_cases/function__too_few_args_one.py +0 -6
  676. package/ref-monty/crates/monty/test_cases/function__too_few_args_two.py +0 -6
  677. package/ref-monty/crates/monty/test_cases/function__too_many_args_one.py +0 -6
  678. package/ref-monty/crates/monty/test_cases/function__too_many_args_two.py +0 -6
  679. package/ref-monty/crates/monty/test_cases/function__too_many_args_zero.py +0 -6
  680. package/ref-monty/crates/monty/test_cases/global__error_assigned_before.py +0 -7
  681. package/ref-monty/crates/monty/test_cases/global__ops.py +0 -163
  682. package/ref-monty/crates/monty/test_cases/hash__dict_unhashable.py +0 -2
  683. package/ref-monty/crates/monty/test_cases/hash__list_unhashable.py +0 -2
  684. package/ref-monty/crates/monty/test_cases/hash__ops.py +0 -153
  685. package/ref-monty/crates/monty/test_cases/id__bytes_literals_distinct.py +0 -3
  686. package/ref-monty/crates/monty/test_cases/id__int_copy_distinct.py +0 -5
  687. package/ref-monty/crates/monty/test_cases/id__is_number_is_number.py +0 -3
  688. package/ref-monty/crates/monty/test_cases/id__non_overlapping_lifetimes_distinct_types.py +0 -10
  689. package/ref-monty/crates/monty/test_cases/id__non_overlapping_lifetimes_same_types.py +0 -6
  690. package/ref-monty/crates/monty/test_cases/id__ops.py +0 -97
  691. package/ref-monty/crates/monty/test_cases/id__str_literals_same.py +0 -3
  692. package/ref-monty/crates/monty/test_cases/if__elif_else.py +0 -207
  693. package/ref-monty/crates/monty/test_cases/if__raise_elif.py +0 -11
  694. package/ref-monty/crates/monty/test_cases/if__raise_else.py +0 -13
  695. package/ref-monty/crates/monty/test_cases/if__raise_if.py +0 -9
  696. package/ref-monty/crates/monty/test_cases/if__raise_in_elif_condition.py +0 -18
  697. package/ref-monty/crates/monty/test_cases/if__raise_in_if_condition.py +0 -16
  698. package/ref-monty/crates/monty/test_cases/if_else_expr__all.py +0 -55
  699. package/ref-monty/crates/monty/test_cases/import__error_cannot_import.py +0 -9
  700. package/ref-monty/crates/monty/test_cases/import__error_module_not_found.py +0 -9
  701. package/ref-monty/crates/monty/test_cases/import__local_scope.py +0 -68
  702. package/ref-monty/crates/monty/test_cases/import__os.py +0 -25
  703. package/ref-monty/crates/monty/test_cases/import__relative_error.py +0 -9
  704. package/ref-monty/crates/monty/test_cases/import__relative_no_module_error.py +0 -9
  705. package/ref-monty/crates/monty/test_cases/import__runtime_error_when_executed.py +0 -14
  706. package/ref-monty/crates/monty/test_cases/import__star_error.py +0 -11
  707. package/ref-monty/crates/monty/test_cases/import__sys.py +0 -47
  708. package/ref-monty/crates/monty/test_cases/import__sys_monty.py +0 -28
  709. package/ref-monty/crates/monty/test_cases/import__type_checking_guard.py +0 -37
  710. package/ref-monty/crates/monty/test_cases/import__typing.py +0 -25
  711. package/ref-monty/crates/monty/test_cases/import__typing_type_ignore.py +0 -4
  712. package/ref-monty/crates/monty/test_cases/int__bigint.py +0 -467
  713. package/ref-monty/crates/monty/test_cases/int__bigint_errors.py +0 -260
  714. package/ref-monty/crates/monty/test_cases/int__ops.py +0 -219
  715. package/ref-monty/crates/monty/test_cases/int__overflow_division.py +0 -84
  716. package/ref-monty/crates/monty/test_cases/is_variant__all.py +0 -36
  717. package/ref-monty/crates/monty/test_cases/isinstance__arg2_list_error.py +0 -2
  718. package/ref-monty/crates/monty/test_cases/isinstance__arg2_type_error.py +0 -2
  719. package/ref-monty/crates/monty/test_cases/iter__dict_mutation.py +0 -4
  720. package/ref-monty/crates/monty/test_cases/iter__for.py +0 -243
  721. package/ref-monty/crates/monty/test_cases/iter__for_loop_unpacking.py +0 -66
  722. package/ref-monty/crates/monty/test_cases/iter__generator_expr.py +0 -20
  723. package/ref-monty/crates/monty/test_cases/iter__generator_expr_type.py +0 -7
  724. package/ref-monty/crates/monty/test_cases/iter__not_iterable.py +0 -3
  725. package/ref-monty/crates/monty/test_cases/lambda__all.py +0 -145
  726. package/ref-monty/crates/monty/test_cases/list__extend_not_iterable.py +0 -7
  727. package/ref-monty/crates/monty/test_cases/list__getitem_out_of_bounds.py +0 -3
  728. package/ref-monty/crates/monty/test_cases/list__index_not_found.py +0 -9
  729. package/ref-monty/crates/monty/test_cases/list__index_start_gt_end.py +0 -10
  730. package/ref-monty/crates/monty/test_cases/list__ops.py +0 -473
  731. package/ref-monty/crates/monty/test_cases/list__pop_empty.py +0 -9
  732. package/ref-monty/crates/monty/test_cases/list__pop_out_of_range.py +0 -9
  733. package/ref-monty/crates/monty/test_cases/list__pop_type_error.py +0 -9
  734. package/ref-monty/crates/monty/test_cases/list__remove_not_found.py +0 -9
  735. package/ref-monty/crates/monty/test_cases/list__setitem_dict_index.py +0 -13
  736. package/ref-monty/crates/monty/test_cases/list__setitem_huge_int_index.py +0 -13
  737. package/ref-monty/crates/monty/test_cases/list__setitem_index_error.py +0 -10
  738. package/ref-monty/crates/monty/test_cases/list__setitem_type_error.py +0 -10
  739. package/ref-monty/crates/monty/test_cases/list__unpack_type_error.py +0 -2
  740. package/ref-monty/crates/monty/test_cases/longint__index_error.py +0 -3
  741. package/ref-monty/crates/monty/test_cases/longint__repeat_error.py +0 -3
  742. package/ref-monty/crates/monty/test_cases/loop__break_continue.py +0 -113
  743. package/ref-monty/crates/monty/test_cases/loop__break_finally.py +0 -69
  744. package/ref-monty/crates/monty/test_cases/loop__break_in_function_error.py +0 -13
  745. package/ref-monty/crates/monty/test_cases/loop__break_in_if_error.py +0 -11
  746. package/ref-monty/crates/monty/test_cases/loop__break_nested_except_clears.py +0 -55
  747. package/ref-monty/crates/monty/test_cases/loop__break_outside_error.py +0 -9
  748. package/ref-monty/crates/monty/test_cases/loop__continue_finally.py +0 -81
  749. package/ref-monty/crates/monty/test_cases/loop__continue_in_function_error.py +0 -13
  750. package/ref-monty/crates/monty/test_cases/loop__continue_in_if_error.py +0 -11
  751. package/ref-monty/crates/monty/test_cases/loop__continue_nested_except_clears.py +0 -60
  752. package/ref-monty/crates/monty/test_cases/loop__continue_outside_error.py +0 -9
  753. package/ref-monty/crates/monty/test_cases/math__acos_domain_error.py +0 -11
  754. package/ref-monty/crates/monty/test_cases/math__acosh_domain_error.py +0 -11
  755. package/ref-monty/crates/monty/test_cases/math__asin_domain_error.py +0 -11
  756. package/ref-monty/crates/monty/test_cases/math__atanh_domain_error.py +0 -11
  757. package/ref-monty/crates/monty/test_cases/math__cos_inf_error.py +0 -11
  758. package/ref-monty/crates/monty/test_cases/math__cosh_overflow_error.py +0 -11
  759. package/ref-monty/crates/monty/test_cases/math__exp_overflow_error.py +0 -11
  760. package/ref-monty/crates/monty/test_cases/math__factorial_float_error.py +0 -11
  761. package/ref-monty/crates/monty/test_cases/math__factorial_negative_error.py +0 -11
  762. package/ref-monty/crates/monty/test_cases/math__floor_inf_error.py +0 -11
  763. package/ref-monty/crates/monty/test_cases/math__floor_nan_error.py +0 -11
  764. package/ref-monty/crates/monty/test_cases/math__floor_str_error.py +0 -11
  765. package/ref-monty/crates/monty/test_cases/math__fmod_inf_error.py +0 -11
  766. package/ref-monty/crates/monty/test_cases/math__gamma_neg_int_error.py +0 -11
  767. package/ref-monty/crates/monty/test_cases/math__gcd_float_error.py +0 -11
  768. package/ref-monty/crates/monty/test_cases/math__isqrt_negative_error.py +0 -11
  769. package/ref-monty/crates/monty/test_cases/math__ldexp_overflow_error.py +0 -11
  770. package/ref-monty/crates/monty/test_cases/math__log1p_domain_error.py +0 -11
  771. package/ref-monty/crates/monty/test_cases/math__log_base1_error.py +0 -11
  772. package/ref-monty/crates/monty/test_cases/math__log_zero_error.py +0 -11
  773. package/ref-monty/crates/monty/test_cases/math__module.py +0 -1432
  774. package/ref-monty/crates/monty/test_cases/math__pow_domain_error.py +0 -11
  775. package/ref-monty/crates/monty/test_cases/math__sin_inf_error.py +0 -11
  776. package/ref-monty/crates/monty/test_cases/math__sqrt_negative_error.py +0 -11
  777. package/ref-monty/crates/monty/test_cases/math__tan_inf_error.py +0 -11
  778. package/ref-monty/crates/monty/test_cases/math__trunc_str_error.py +0 -11
  779. package/ref-monty/crates/monty/test_cases/method__args_kwargs_unpacking.py +0 -259
  780. package/ref-monty/crates/monty/test_cases/name_error__unbound_local_func.py +0 -19
  781. package/ref-monty/crates/monty/test_cases/name_error__unbound_local_module.py +0 -12
  782. package/ref-monty/crates/monty/test_cases/name_error__undefined_call_chained.py +0 -9
  783. package/ref-monty/crates/monty/test_cases/name_error__undefined_call_in_expr.py +0 -9
  784. package/ref-monty/crates/monty/test_cases/name_error__undefined_call_in_function.py +0 -16
  785. package/ref-monty/crates/monty/test_cases/name_error__undefined_call_with_args.py +0 -9
  786. package/ref-monty/crates/monty/test_cases/name_error__undefined_global.py +0 -10
  787. package/ref-monty/crates/monty/test_cases/namedtuple__missing_attr.py +0 -11
  788. package/ref-monty/crates/monty/test_cases/namedtuple__ops.py +0 -34
  789. package/ref-monty/crates/monty/test_cases/nonlocal__error_module_level.py +0 -3
  790. package/ref-monty/crates/monty/test_cases/nonlocal__ops.py +0 -353
  791. package/ref-monty/crates/monty/test_cases/os__environ.py +0 -40
  792. package/ref-monty/crates/monty/test_cases/os__getenv_key_list_error.py +0 -5
  793. package/ref-monty/crates/monty/test_cases/os__getenv_key_type_error.py +0 -5
  794. package/ref-monty/crates/monty/test_cases/parse_error__complex.py +0 -3
  795. package/ref-monty/crates/monty/test_cases/pathlib__import.py +0 -11
  796. package/ref-monty/crates/monty/test_cases/pathlib__os.py +0 -136
  797. package/ref-monty/crates/monty/test_cases/pathlib__os_read_error.py +0 -12
  798. package/ref-monty/crates/monty/test_cases/pathlib__pure.py +0 -81
  799. package/ref-monty/crates/monty/test_cases/pyobject__cycle_dict_self.py +0 -5
  800. package/ref-monty/crates/monty/test_cases/pyobject__cycle_list_dict.py +0 -6
  801. package/ref-monty/crates/monty/test_cases/pyobject__cycle_list_self.py +0 -5
  802. package/ref-monty/crates/monty/test_cases/pyobject__cycle_multiple_refs.py +0 -6
  803. package/ref-monty/crates/monty/test_cases/range__error_no_args.py +0 -2
  804. package/ref-monty/crates/monty/test_cases/range__error_step_zero.py +0 -2
  805. package/ref-monty/crates/monty/test_cases/range__error_too_many_args.py +0 -2
  806. package/ref-monty/crates/monty/test_cases/range__getitem_index_error.py +0 -10
  807. package/ref-monty/crates/monty/test_cases/range__ops.py +0 -236
  808. package/ref-monty/crates/monty/test_cases/re__basic.py +0 -756
  809. package/ref-monty/crates/monty/test_cases/re__grouping.py +0 -241
  810. package/ref-monty/crates/monty/test_cases/re__match.py +0 -148
  811. package/ref-monty/crates/monty/test_cases/recursion__deep_drop.py +0 -26
  812. package/ref-monty/crates/monty/test_cases/recursion__deep_eq.py +0 -23
  813. package/ref-monty/crates/monty/test_cases/recursion__deep_hash.py +0 -46
  814. package/ref-monty/crates/monty/test_cases/recursion__deep_repr.py +0 -12
  815. package/ref-monty/crates/monty/test_cases/recursion__function_depth.py +0 -13
  816. package/ref-monty/crates/monty/test_cases/refcount__cycle_mutual_reference.py +0 -18
  817. package/ref-monty/crates/monty/test_cases/refcount__cycle_self_reference.py +0 -12
  818. package/ref-monty/crates/monty/test_cases/refcount__dict_basic.py +0 -5
  819. package/ref-monty/crates/monty/test_cases/refcount__dict_get.py +0 -5
  820. package/ref-monty/crates/monty/test_cases/refcount__dict_keys_and.py +0 -14
  821. package/ref-monty/crates/monty/test_cases/refcount__dict_overwrite.py +0 -6
  822. package/ref-monty/crates/monty/test_cases/refcount__gather_cleanup.py +0 -16
  823. package/ref-monty/crates/monty/test_cases/refcount__gather_exception.py +0 -18
  824. package/ref-monty/crates/monty/test_cases/refcount__gather_nested_cancel.py +0 -25
  825. package/ref-monty/crates/monty/test_cases/refcount__immediate_skipped.py +0 -4
  826. package/ref-monty/crates/monty/test_cases/refcount__kwargs_unpacking.py +0 -27
  827. package/ref-monty/crates/monty/test_cases/refcount__list_append_multiple.py +0 -6
  828. package/ref-monty/crates/monty/test_cases/refcount__list_append_ref.py +0 -5
  829. package/ref-monty/crates/monty/test_cases/refcount__list_concat.py +0 -5
  830. package/ref-monty/crates/monty/test_cases/refcount__list_getitem.py +0 -5
  831. package/ref-monty/crates/monty/test_cases/refcount__list_iadd.py +0 -5
  832. package/ref-monty/crates/monty/test_cases/refcount__nested_list.py +0 -4
  833. package/ref-monty/crates/monty/test_cases/refcount__re_pattern_sub_error_paths.py +0 -37
  834. package/ref-monty/crates/monty/test_cases/refcount__re_search_match.py +0 -34
  835. package/ref-monty/crates/monty/test_cases/refcount__re_sub_error_paths.py +0 -31
  836. package/ref-monty/crates/monty/test_cases/refcount__shared_reference.py +0 -4
  837. package/ref-monty/crates/monty/test_cases/refcount__single_list.py +0 -3
  838. package/ref-monty/crates/monty/test_cases/repr__cycle_detection.py +0 -24
  839. package/ref-monty/crates/monty/test_cases/set__ops.py +0 -191
  840. package/ref-monty/crates/monty/test_cases/set__review_bugs.py +0 -35
  841. package/ref-monty/crates/monty/test_cases/set__unpack_type_error.py +0 -2
  842. package/ref-monty/crates/monty/test_cases/slice__invalid_indices.py +0 -2
  843. package/ref-monty/crates/monty/test_cases/slice__kwargs.py +0 -9
  844. package/ref-monty/crates/monty/test_cases/slice__no_args.py +0 -9
  845. package/ref-monty/crates/monty/test_cases/slice__ops.py +0 -149
  846. package/ref-monty/crates/monty/test_cases/slice__step_zero.py +0 -9
  847. package/ref-monty/crates/monty/test_cases/slice__step_zero_bytes.py +0 -9
  848. package/ref-monty/crates/monty/test_cases/slice__step_zero_range.py +0 -9
  849. package/ref-monty/crates/monty/test_cases/slice__step_zero_str.py +0 -9
  850. package/ref-monty/crates/monty/test_cases/slice__step_zero_tuple.py +0 -9
  851. package/ref-monty/crates/monty/test_cases/slice__too_many_args.py +0 -9
  852. package/ref-monty/crates/monty/test_cases/str__getitem_index_error.py +0 -10
  853. package/ref-monty/crates/monty/test_cases/str__index_not_found.py +0 -9
  854. package/ref-monty/crates/monty/test_cases/str__join_no_args.py +0 -9
  855. package/ref-monty/crates/monty/test_cases/str__join_non_string.py +0 -9
  856. package/ref-monty/crates/monty/test_cases/str__join_not_iterable.py +0 -9
  857. package/ref-monty/crates/monty/test_cases/str__join_too_many_args.py +0 -9
  858. package/ref-monty/crates/monty/test_cases/str__methods.py +0 -327
  859. package/ref-monty/crates/monty/test_cases/str__ops.py +0 -162
  860. package/ref-monty/crates/monty/test_cases/str__partition_empty.py +0 -9
  861. package/ref-monty/crates/monty/test_cases/str__rsplit_empty_sep.py +0 -9
  862. package/ref-monty/crates/monty/test_cases/str__split_empty_sep.py +0 -9
  863. package/ref-monty/crates/monty/test_cases/sys__types.py +0 -7
  864. package/ref-monty/crates/monty/test_cases/traceback__division_error.py +0 -30
  865. package/ref-monty/crates/monty/test_cases/traceback__index_error.py +0 -17
  866. package/ref-monty/crates/monty/test_cases/traceback__insert_as_int.py +0 -10
  867. package/ref-monty/crates/monty/test_cases/traceback__nested_call.py +0 -29
  868. package/ref-monty/crates/monty/test_cases/traceback__nonlocal_module_scope.py +0 -10
  869. package/ref-monty/crates/monty/test_cases/traceback__nonlocal_unbound.py +0 -24
  870. package/ref-monty/crates/monty/test_cases/traceback__range_as_int.py +0 -9
  871. package/ref-monty/crates/monty/test_cases/traceback__recursion_error.py +0 -23
  872. package/ref-monty/crates/monty/test_cases/traceback__set_mutation.py +0 -11
  873. package/ref-monty/crates/monty/test_cases/traceback__undefined_attr_call.py +0 -16
  874. package/ref-monty/crates/monty/test_cases/traceback__undefined_call.py +0 -16
  875. package/ref-monty/crates/monty/test_cases/traceback__undefined_raise.py +0 -16
  876. package/ref-monty/crates/monty/test_cases/try_except__all.py +0 -472
  877. package/ref-monty/crates/monty/test_cases/try_except__bare_raise_no_context.py +0 -2
  878. package/ref-monty/crates/monty/test_cases/try_except__invalid_type.py +0 -5
  879. package/ref-monty/crates/monty/test_cases/tuple__getitem_out_of_bounds.py +0 -3
  880. package/ref-monty/crates/monty/test_cases/tuple__index_not_found.py +0 -9
  881. package/ref-monty/crates/monty/test_cases/tuple__index_start_gt_end.py +0 -10
  882. package/ref-monty/crates/monty/test_cases/tuple__methods.py +0 -19
  883. package/ref-monty/crates/monty/test_cases/tuple__ops.py +0 -133
  884. package/ref-monty/crates/monty/test_cases/tuple__unpack_type_error.py +0 -2
  885. package/ref-monty/crates/monty/test_cases/type__builtin_attr_error.py +0 -9
  886. package/ref-monty/crates/monty/test_cases/type__bytes_negative.py +0 -2
  887. package/ref-monty/crates/monty/test_cases/type__cell_not_builtin.py +0 -9
  888. package/ref-monty/crates/monty/test_cases/type__exception_attr_error.py +0 -11
  889. package/ref-monty/crates/monty/test_cases/type__float_conversion_error.py +0 -2
  890. package/ref-monty/crates/monty/test_cases/type__float_repr_both_quotes.py +0 -9
  891. package/ref-monty/crates/monty/test_cases/type__float_repr_newline.py +0 -9
  892. package/ref-monty/crates/monty/test_cases/type__float_repr_single_quote.py +0 -9
  893. package/ref-monty/crates/monty/test_cases/type__int_conversion_error.py +0 -2
  894. package/ref-monty/crates/monty/test_cases/type__list_not_iterable.py +0 -2
  895. package/ref-monty/crates/monty/test_cases/type__non_builtin_name_error.py +0 -9
  896. package/ref-monty/crates/monty/test_cases/type__ops.py +0 -200
  897. package/ref-monty/crates/monty/test_cases/type__shadow_exc.py +0 -3
  898. package/ref-monty/crates/monty/test_cases/type__shadow_int.py +0 -9
  899. package/ref-monty/crates/monty/test_cases/type__shadow_len.py +0 -3
  900. package/ref-monty/crates/monty/test_cases/type__tuple_not_iterable.py +0 -2
  901. package/ref-monty/crates/monty/test_cases/type_error__int_add_list.py +0 -2
  902. package/ref-monty/crates/monty/test_cases/type_error__int_div_str.py +0 -2
  903. package/ref-monty/crates/monty/test_cases/type_error__int_floordiv_str.py +0 -2
  904. package/ref-monty/crates/monty/test_cases/type_error__int_iadd_str.py +0 -3
  905. package/ref-monty/crates/monty/test_cases/type_error__int_mod_str.py +0 -2
  906. package/ref-monty/crates/monty/test_cases/type_error__int_pow_str.py +0 -2
  907. package/ref-monty/crates/monty/test_cases/type_error__int_sub_str.py +0 -2
  908. package/ref-monty/crates/monty/test_cases/type_error__list_add_int.py +0 -2
  909. package/ref-monty/crates/monty/test_cases/type_error__list_add_str.py +0 -2
  910. package/ref-monty/crates/monty/test_cases/type_error__list_iadd_int.py +0 -6
  911. package/ref-monty/crates/monty/test_cases/type_error__str_add_int.py +0 -2
  912. package/ref-monty/crates/monty/test_cases/type_error__str_iadd_int.py +0 -3
  913. package/ref-monty/crates/monty/test_cases/type_error__unary_invert_str.py +0 -3
  914. package/ref-monty/crates/monty/test_cases/type_error__unary_minus_str.py +0 -4
  915. package/ref-monty/crates/monty/test_cases/type_error__unary_neg_str.py +0 -3
  916. package/ref-monty/crates/monty/test_cases/type_error__unary_plus_str.py +0 -4
  917. package/ref-monty/crates/monty/test_cases/typing__types.py +0 -24
  918. package/ref-monty/crates/monty/test_cases/unpack__nested.py +0 -48
  919. package/ref-monty/crates/monty/test_cases/unpack__non_sequence.py +0 -9
  920. package/ref-monty/crates/monty/test_cases/unpack__not_enough.py +0 -9
  921. package/ref-monty/crates/monty/test_cases/unpack__ops.py +0 -153
  922. package/ref-monty/crates/monty/test_cases/unpack__star_not_enough.py +0 -9
  923. package/ref-monty/crates/monty/test_cases/unpack__too_many.py +0 -9
  924. package/ref-monty/crates/monty/test_cases/version__cpython.py +0 -4
  925. package/ref-monty/crates/monty/test_cases/walrus__all.py +0 -178
  926. package/ref-monty/crates/monty/test_cases/while__all.py +0 -206
  927. package/ref-monty/crates/monty/tests/asyncio.rs +0 -764
  928. package/ref-monty/crates/monty/tests/binary_serde.rs +0 -185
  929. package/ref-monty/crates/monty/tests/bytecode_limits.rs +0 -248
  930. package/ref-monty/crates/monty/tests/datatest_runner.rs +0 -2029
  931. package/ref-monty/crates/monty/tests/inputs.rs +0 -420
  932. package/ref-monty/crates/monty/tests/json_serde.rs +0 -250
  933. package/ref-monty/crates/monty/tests/main.rs +0 -71
  934. package/ref-monty/crates/monty/tests/math_module.rs +0 -114
  935. package/ref-monty/crates/monty/tests/name_lookup.rs +0 -482
  936. package/ref-monty/crates/monty/tests/os_tests.rs +0 -459
  937. package/ref-monty/crates/monty/tests/parse_errors.rs +0 -441
  938. package/ref-monty/crates/monty/tests/print_writer.rs +0 -238
  939. package/ref-monty/crates/monty/tests/py_object.rs +0 -121
  940. package/ref-monty/crates/monty/tests/regex.rs +0 -90
  941. package/ref-monty/crates/monty/tests/repl.rs +0 -344
  942. package/ref-monty/crates/monty/tests/resource_limits.rs +0 -1826
  943. package/ref-monty/crates/monty/tests/try_from.rs +0 -167
  944. package/ref-monty/crates/monty-cli/Cargo.toml +0 -25
  945. package/ref-monty/crates/monty-cli/src/main.rs +0 -541
  946. package/ref-monty/crates/monty-js/.cargo/config.toml +0 -2
  947. package/ref-monty/crates/monty-js/.prettierignore +0 -8
  948. package/ref-monty/crates/monty-js/Cargo.toml +0 -32
  949. package/ref-monty/crates/monty-js/README.md +0 -207
  950. package/ref-monty/crates/monty-js/__test__/async.spec.ts +0 -350
  951. package/ref-monty/crates/monty-js/__test__/basic.spec.ts +0 -114
  952. package/ref-monty/crates/monty-js/__test__/exceptions.spec.ts +0 -427
  953. package/ref-monty/crates/monty-js/__test__/external.spec.ts +0 -354
  954. package/ref-monty/crates/monty-js/__test__/inputs.spec.ts +0 -143
  955. package/ref-monty/crates/monty-js/__test__/limits.spec.ts +0 -162
  956. package/ref-monty/crates/monty-js/__test__/package.json +0 -3
  957. package/ref-monty/crates/monty-js/__test__/print.spec.ts +0 -229
  958. package/ref-monty/crates/monty-js/__test__/repl.spec.ts +0 -34
  959. package/ref-monty/crates/monty-js/__test__/serialize.spec.ts +0 -205
  960. package/ref-monty/crates/monty-js/__test__/start.spec.ts +0 -443
  961. package/ref-monty/crates/monty-js/__test__/type_check.spec.ts +0 -147
  962. package/ref-monty/crates/monty-js/__test__/types.spec.ts +0 -319
  963. package/ref-monty/crates/monty-js/build.rs +0 -61
  964. package/ref-monty/crates/monty-js/index-header.d.ts +0 -3
  965. package/ref-monty/crates/monty-js/package-lock.json +0 -4694
  966. package/ref-monty/crates/monty-js/package.json +0 -100
  967. package/ref-monty/crates/monty-js/scripts/smoke-test.sh +0 -69
  968. package/ref-monty/crates/monty-js/smoke-test/package.json +0 -17
  969. package/ref-monty/crates/monty-js/smoke-test/test.ts +0 -171
  970. package/ref-monty/crates/monty-js/smoke-test/tsconfig.json +0 -11
  971. package/ref-monty/crates/monty-js/src/convert.rs +0 -648
  972. package/ref-monty/crates/monty-js/src/exceptions.rs +0 -293
  973. package/ref-monty/crates/monty-js/src/lib.rs +0 -41
  974. package/ref-monty/crates/monty-js/src/limits.rs +0 -53
  975. package/ref-monty/crates/monty-js/src/monty_cls.rs +0 -1407
  976. package/ref-monty/crates/monty-js/tsconfig.json +0 -17
  977. package/ref-monty/crates/monty-js/wrapper.ts +0 -701
  978. package/ref-monty/crates/monty-python/Cargo.toml +0 -38
  979. package/ref-monty/crates/monty-python/README.md +0 -134
  980. package/ref-monty/crates/monty-python/build.rs +0 -4
  981. package/ref-monty/crates/monty-python/example.py +0 -40
  982. package/ref-monty/crates/monty-python/exercise.py +0 -46
  983. package/ref-monty/crates/monty-python/pyproject.toml +0 -57
  984. package/ref-monty/crates/monty-python/python/pydantic_monty/__init__.py +0 -281
  985. package/ref-monty/crates/monty-python/python/pydantic_monty/_monty.pyi +0 -677
  986. package/ref-monty/crates/monty-python/python/pydantic_monty/os_access.py +0 -933
  987. package/ref-monty/crates/monty-python/python/pydantic_monty/py.typed +0 -0
  988. package/ref-monty/crates/monty-python/src/convert.rs +0 -273
  989. package/ref-monty/crates/monty-python/src/dataclass.rs +0 -461
  990. package/ref-monty/crates/monty-python/src/exceptions.rs +0 -557
  991. package/ref-monty/crates/monty-python/src/external.rs +0 -165
  992. package/ref-monty/crates/monty-python/src/lib.rs +0 -77
  993. package/ref-monty/crates/monty-python/src/limits.rs +0 -142
  994. package/ref-monty/crates/monty-python/src/monty_cls.rs +0 -1650
  995. package/ref-monty/crates/monty-python/src/repl.rs +0 -470
  996. package/ref-monty/crates/monty-python/src/serialization.rs +0 -761
  997. package/ref-monty/crates/monty-python/tests/test_async.py +0 -1201
  998. package/ref-monty/crates/monty-python/tests/test_basic.py +0 -66
  999. package/ref-monty/crates/monty-python/tests/test_dataclasses.py +0 -971
  1000. package/ref-monty/crates/monty-python/tests/test_exceptions.py +0 -361
  1001. package/ref-monty/crates/monty-python/tests/test_external.py +0 -367
  1002. package/ref-monty/crates/monty-python/tests/test_inputs.py +0 -126
  1003. package/ref-monty/crates/monty-python/tests/test_limits.py +0 -257
  1004. package/ref-monty/crates/monty-python/tests/test_os_access.py +0 -1286
  1005. package/ref-monty/crates/monty-python/tests/test_os_access_compat.py +0 -731
  1006. package/ref-monty/crates/monty-python/tests/test_os_access_raw.py +0 -483
  1007. package/ref-monty/crates/monty-python/tests/test_os_calls.py +0 -819
  1008. package/ref-monty/crates/monty-python/tests/test_print.py +0 -208
  1009. package/ref-monty/crates/monty-python/tests/test_re.py +0 -170
  1010. package/ref-monty/crates/monty-python/tests/test_readme_examples.py +0 -20
  1011. package/ref-monty/crates/monty-python/tests/test_repl.py +0 -749
  1012. package/ref-monty/crates/monty-python/tests/test_serialize.py +0 -284
  1013. package/ref-monty/crates/monty-python/tests/test_start.py +0 -346
  1014. package/ref-monty/crates/monty-python/tests/test_threading.py +0 -163
  1015. package/ref-monty/crates/monty-python/tests/test_type_check.py +0 -344
  1016. package/ref-monty/crates/monty-python/tests/test_types.py +0 -553
  1017. package/ref-monty/crates/monty-type-checking/Cargo.toml +0 -32
  1018. package/ref-monty/crates/monty-type-checking/src/db.rs +0 -116
  1019. package/ref-monty/crates/monty-type-checking/src/lib.rs +0 -4
  1020. package/ref-monty/crates/monty-type-checking/src/type_check.rs +0 -280
  1021. package/ref-monty/crates/monty-type-checking/tests/bad_types.py +0 -109
  1022. package/ref-monty/crates/monty-type-checking/tests/bad_types_output.txt +0 -21
  1023. package/ref-monty/crates/monty-type-checking/tests/good_types.py +0 -475
  1024. package/ref-monty/crates/monty-type-checking/tests/main.rs +0 -205
  1025. package/ref-monty/crates/monty-type-checking/tests/reveal_types.py +0 -56
  1026. package/ref-monty/crates/monty-type-checking/tests/reveal_types_output.txt +0 -41
  1027. package/ref-monty/crates/monty-typeshed/Cargo.toml +0 -29
  1028. package/ref-monty/crates/monty-typeshed/README.md +0 -11
  1029. package/ref-monty/crates/monty-typeshed/build.rs +0 -101
  1030. package/ref-monty/crates/monty-typeshed/custom/README.md +0 -1
  1031. package/ref-monty/crates/monty-typeshed/custom/asyncio.pyi +0 -138
  1032. package/ref-monty/crates/monty-typeshed/custom/os.pyi +0 -87
  1033. package/ref-monty/crates/monty-typeshed/custom/sys.pyi +0 -33
  1034. package/ref-monty/crates/monty-typeshed/src/lib.rs +0 -56
  1035. package/ref-monty/crates/monty-typeshed/update.py +0 -321
  1036. package/ref-monty/crates/monty-typeshed/vendor/typeshed/source_commit.txt +0 -1
  1037. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/VERSIONS +0 -20
  1038. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/_collections_abc.pyi +0 -105
  1039. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/_typeshed/__init__.pyi +0 -394
  1040. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/asyncio.pyi +0 -138
  1041. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/builtins.pyi +0 -1434
  1042. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/collections/__init__.pyi +0 -527
  1043. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/collections/abc.pyi +0 -2
  1044. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/dataclasses.pyi +0 -502
  1045. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/enum.pyi +0 -376
  1046. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/math.pyi +0 -149
  1047. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/os.pyi +0 -87
  1048. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/pathlib/__init__.pyi +0 -395
  1049. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/pathlib/types.pyi +0 -8
  1050. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/re.pyi +0 -337
  1051. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/sys.pyi +0 -33
  1052. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/types.pyi +0 -741
  1053. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/typing.pyi +0 -1217
  1054. package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/typing_extensions.pyi +0 -716
  1055. package/ref-monty/docs/usage-guide.md +0 -117
  1056. package/ref-monty/examples/README.md +0 -3
  1057. package/ref-monty/examples/expense_analysis/README.md +0 -3
  1058. package/ref-monty/examples/expense_analysis/data.py +0 -124
  1059. package/ref-monty/examples/expense_analysis/main.py +0 -115
  1060. package/ref-monty/examples/sql_playground/README.md +0 -20
  1061. package/ref-monty/examples/sql_playground/external_functions.py +0 -129
  1062. package/ref-monty/examples/sql_playground/main.py +0 -81
  1063. package/ref-monty/examples/sql_playground/sandbox_code.py +0 -82
  1064. package/ref-monty/examples/sql_playground/type_stubs.pyi +0 -14
  1065. package/ref-monty/examples/web_scraper/README.md +0 -15
  1066. package/ref-monty/examples/web_scraper/browser.py +0 -56
  1067. package/ref-monty/examples/web_scraper/example_code.py +0 -59
  1068. package/ref-monty/examples/web_scraper/external_functions.py +0 -324
  1069. package/ref-monty/examples/web_scraper/main.py +0 -193
  1070. package/ref-monty/examples/web_scraper/sub_agent.py +0 -79
  1071. package/ref-monty/monty-npm.md +0 -235
  1072. package/ref-monty/pyproject.toml +0 -162
  1073. package/ref-monty/scripts/check_imports.py +0 -91
  1074. package/ref-monty/scripts/codecov_diff.py +0 -412
  1075. package/ref-monty/scripts/complete_tests.py +0 -146
  1076. package/ref-monty/scripts/flamegraph_to_text.py +0 -208
  1077. package/ref-monty/scripts/iter_test_methods.py +0 -540
  1078. package/ref-monty/scripts/run_traceback.py +0 -180
  1079. package/ref-monty/scripts/startup_performance.py +0 -130
  1080. package/ref-monty/uv.lock +0 -1779
  1081. /package/docs/{plugin-examples.md → plugins-examples.md} +0 -0
@@ -1,3206 +0,0 @@
1
- //! Bytecode compiler for transforming AST to bytecode.
2
- //!
3
- //! The compiler traverses the prepared AST (`PreparedNode` and `Expr` types from `expressions.rs`)
4
- //! and emits bytecode instructions using `CodeBuilder`. It handles variable scoping,
5
- //! control flow, and expression evaluation order following Python semantics.
6
- //!
7
- //! Functions are compiled recursively: when a `PreparedFunctionDef` is encountered,
8
- //! its body is compiled to bytecode and a `Function` struct is created. All compiled
9
- //! functions are collected and returned along with the module code.
10
-
11
- use std::borrow::Cow;
12
-
13
- use super::{
14
- builder::{CodeBuilder, JumpLabel},
15
- code::{Code, ExceptionEntry},
16
- op::Opcode,
17
- };
18
- use crate::{
19
- args::{ArgExprs, CallArg, CallKwarg, Kwarg},
20
- builtins::Builtins,
21
- exception_private::ExcType,
22
- exception_public::{MontyException, StackFrame},
23
- expressions::{
24
- Callable, CmpOperator, Comprehension, DictItem, Expr, ExprLoc, Identifier, Literal, NameScope, Node, Operator,
25
- PreparedFunctionDef, PreparedNode, SequenceItem, UnpackTarget,
26
- },
27
- fstring::{ConversionFlag, FStringPart, FormatSpec, ParsedFormatSpec, encode_format_spec},
28
- function::Function,
29
- intern::{Interns, StringId},
30
- modules::BuiltinModule,
31
- parse::{CodeRange, ExceptHandler, Try},
32
- value::{EitherStr, Value},
33
- };
34
-
35
- /// Maximum number of arguments allowed in a function call.
36
- ///
37
- /// This limit comes from the bytecode format: `CallFunction` and `CallAttr`
38
- /// use a u8 operand for the argument count, so max 255. Python itself has no
39
- /// such limit but we need one for our bytecode encoding.
40
- const MAX_CALL_ARGS: usize = 255;
41
-
42
- /// Compiles prepared AST nodes to bytecode.
43
- ///
44
- /// The compiler traverses the AST and emits bytecode instructions using
45
- /// `CodeBuilder`. It handles variable scoping, control flow, and expression
46
- /// evaluation order following Python semantics.
47
- ///
48
- /// Functions are compiled recursively and collected in the `functions` vector.
49
- /// When a `PreparedFunctionDef` is encountered, its body is compiled first,
50
- /// creating a `Function` struct that is added to the vector. The index of the
51
- /// function in this vector becomes the operand for MakeFunction/MakeClosure opcodes.
52
- pub struct Compiler<'a> {
53
- /// Current code being built.
54
- code: CodeBuilder,
55
-
56
- /// Reference to interns for string/function lookups.
57
- interns: &'a Interns,
58
-
59
- /// Compiled functions, indexed by their position in this vector.
60
- ///
61
- /// Functions are added in the order they are encountered during compilation.
62
- /// Nested functions are compiled before their containing function's code
63
- /// finishes, so inner functions have lower indices.
64
- functions: Vec<Function>,
65
-
66
- /// Loop stack for break/continue handling.
67
- /// Each entry tracks the loop start offset and pending break jumps.
68
- loop_stack: Vec<LoopInfo>,
69
-
70
- /// Stack of finally targets for handling returns inside try-finally.
71
- ///
72
- /// When a return statement is compiled inside a try-finally block, instead
73
- /// of immediately returning, we store the return value and jump to the
74
- /// finally block. The finally block will then execute the return.
75
- finally_targets: Vec<FinallyTarget>,
76
-
77
- /// Tracks nesting depth inside exception handlers.
78
- ///
79
- /// When break/continue/return is inside an except handler, we need to
80
- /// clear the current exception (`ClearException`) and pop the exception
81
- /// value from the stack before jumping to the finally path or loop target.
82
- except_handler_depth: usize,
83
-
84
- /// Whether the compiler is currently compiling module-level code.
85
- ///
86
- /// At module level, `Local` and `LocalUnassigned` scopes map to global opcodes
87
- /// (`LoadGlobal`/`StoreGlobal`/`DeleteGlobal`) because module locals live in the
88
- /// globals array. In function bodies this is `false` and these scopes use local
89
- /// opcodes that index into the stack.
90
- is_module_scope: bool,
91
- }
92
-
93
- /// Information about a loop for break/continue handling.
94
- ///
95
- /// Tracks the bytecode locations needed for compiling break and continue statements:
96
- /// - `start`: where continue should jump to (the ForIter instruction for `for` loops,
97
- /// or condition evaluation for `while` loops)
98
- /// - `break_jumps`: pending jumps from break statements that need to be patched
99
- /// to jump past the loop's else block
100
- /// - `has_iterator_on_stack`: whether this loop has an iterator on the stack that
101
- /// needs to be popped on break (true for `for` loops, false for `while` loops)
102
- struct LoopInfo {
103
- /// Bytecode offset of loop start (for continue).
104
- start: usize,
105
- /// Jump labels that need patching to loop end (for break).
106
- break_jumps: Vec<JumpLabel>,
107
- /// Whether this loop has an iterator on the stack.
108
- /// True for `for` loops, false for `while` loops.
109
- has_iterator_on_stack: bool,
110
- }
111
-
112
- /// A break or continue that needs to go through a finally block.
113
- ///
114
- /// When break/continue is inside a try-finally, we need to run the finally block
115
- /// before executing the break/continue. This struct tracks the jump and which
116
- /// loop it targets.
117
- struct BreakContinueThruFinally {
118
- /// The jump instruction that needs to be patched.
119
- jump: JumpLabel,
120
- /// The loop depth (index in loop_stack) being targeted.
121
- target_loop_depth: usize,
122
- }
123
-
124
- /// Tracks a finally block for handling returns/break/continue inside try-finally.
125
- ///
126
- /// When compiling a try-finally, we push a `FinallyTarget` to track jumps
127
- /// from return/break/continue statements that need to go through the finally block.
128
- struct FinallyTarget {
129
- /// Jump labels for returns inside the try block that need to go to finally.
130
- return_jumps: Vec<JumpLabel>,
131
- /// Break statements that need to go through this finally block.
132
- break_jumps: Vec<BreakContinueThruFinally>,
133
- /// Continue statements that need to go through this finally block.
134
- continue_jumps: Vec<BreakContinueThruFinally>,
135
- /// The loop depth when this finally was entered.
136
- /// Used to determine if break/continue targets a loop outside this finally.
137
- loop_depth_at_entry: usize,
138
- }
139
-
140
- /// Result of module compilation: the module code and all compiled functions.
141
- pub struct CompileResult {
142
- /// The compiled module code.
143
- pub code: Code,
144
- /// All functions compiled during module compilation, indexed by their function ID.
145
- pub functions: Vec<Function>,
146
- }
147
-
148
- impl<'a> Compiler<'a> {
149
- /// Creates a new compiler with access to the string interner.
150
- fn new(interns: &'a Interns, functions: Vec<Function>) -> Self {
151
- Self {
152
- code: CodeBuilder::new(),
153
- interns,
154
- functions,
155
- loop_stack: Vec::new(),
156
- finally_targets: Vec::new(),
157
- except_handler_depth: 0,
158
- is_module_scope: false,
159
- }
160
- }
161
-
162
- /// Compiles module-level code (a sequence of statements).
163
- ///
164
- /// Returns the compiled module Code and all compiled Functions, or a compile
165
- /// error if limits were exceeded. The module implicitly returns the value
166
- /// of the last expression, or None if empty.
167
- pub fn compile_module(
168
- nodes: &[PreparedNode],
169
- interns: &Interns,
170
- num_locals: u16,
171
- ) -> Result<CompileResult, CompileError> {
172
- Self::compile_module_with_functions(nodes, interns, num_locals, Vec::new())
173
- }
174
-
175
- /// Compiles module-level code while preserving an existing function table prefix.
176
- ///
177
- /// This is used by incremental REPL compilation so previously created
178
- /// `FunctionId`s remain stable: new function IDs are allocated after
179
- /// `existing_functions.len()`.
180
- pub fn compile_module_with_functions(
181
- nodes: &[PreparedNode],
182
- interns: &Interns,
183
- num_locals: u16,
184
- existing_functions: Vec<Function>,
185
- ) -> Result<CompileResult, CompileError> {
186
- let mut compiler = Compiler::new(interns, Vec::new());
187
- compiler.functions = existing_functions;
188
- compiler.is_module_scope = true;
189
- compiler.compile_block(nodes)?;
190
-
191
- // Module returns None if no explicit return
192
- compiler.code.emit(Opcode::LoadNone);
193
- compiler.code.emit(Opcode::ReturnValue);
194
-
195
- Ok(CompileResult {
196
- code: compiler.code.build(num_locals),
197
- functions: compiler.functions,
198
- })
199
- }
200
-
201
- /// Compiles a function body to bytecode, returning the Code and any nested functions.
202
- ///
203
- /// Used internally when compiling function definitions. The function body is
204
- /// compiled to bytecode with an implicit `return None` at the end if there's
205
- /// no explicit return statement.
206
- ///
207
- /// The `functions` parameter receives any previously compiled functions, and
208
- /// any nested functions found in the body will be added to it.
209
- fn compile_function_body(
210
- body: &[PreparedNode],
211
- interns: &Interns,
212
- functions: Vec<Function>,
213
- num_locals: u16,
214
- ) -> Result<(Code, Vec<Function>), CompileError> {
215
- let mut compiler = Compiler::new(interns, functions);
216
- compiler.compile_block(body)?;
217
-
218
- // Implicit return None if no explicit return
219
- compiler.code.emit(Opcode::LoadNone);
220
- compiler.code.emit(Opcode::ReturnValue);
221
-
222
- Ok((compiler.code.build(num_locals), compiler.functions))
223
- }
224
-
225
- /// Compiles a block of statements.
226
- fn compile_block(&mut self, nodes: &[PreparedNode]) -> Result<(), CompileError> {
227
- for node in nodes {
228
- self.compile_stmt(node)?;
229
- }
230
- Ok(())
231
- }
232
-
233
- // ========================================================================
234
- // Statement Compilation
235
- // ========================================================================
236
-
237
- /// Compiles a single statement.
238
- fn compile_stmt(&mut self, node: &PreparedNode) -> Result<(), CompileError> {
239
- // Node is an alias, use qualified path for matching
240
- match node {
241
- Node::Expr(expr) => {
242
- self.compile_expr(expr)?;
243
- self.code.emit(Opcode::Pop); // Discard result
244
- }
245
- Node::Return(expr) => {
246
- self.compile_expr(expr)?;
247
- self.compile_return();
248
- }
249
- Node::ReturnNone => {
250
- self.code.emit(Opcode::LoadNone);
251
- self.compile_return();
252
- }
253
- Node::Assign { target, object } => {
254
- self.compile_expr(object)?;
255
- self.compile_store(target);
256
- }
257
- Node::UnpackAssign {
258
- targets,
259
- targets_position,
260
- object,
261
- } => {
262
- self.compile_expr(object)?;
263
-
264
- // Check if there's a starred target
265
- let star_idx = targets.iter().position(|t| matches!(t, UnpackTarget::Starred(_)));
266
-
267
- // Set location to targets for proper caret in tracebacks
268
- self.code.set_location(*targets_position, None);
269
-
270
- if let Some(star_idx) = star_idx {
271
- // Has starred target - use UnpackEx
272
- let before = u8::try_from(star_idx).expect("too many targets before star");
273
- let after = u8::try_from(targets.len() - star_idx - 1).expect("too many targets after star");
274
- self.code.emit_u8_u8(Opcode::UnpackEx, before, after);
275
- } else {
276
- // No starred target - use UnpackSequence
277
- let count = u8::try_from(targets.len()).expect("too many targets in unpack");
278
- self.code.emit_u8(Opcode::UnpackSequence, count);
279
- }
280
-
281
- // After UnpackSequence/UnpackEx, values are on stack with first item on top
282
- // Store them in order (first target gets first item), handling nesting
283
- for target in targets {
284
- self.compile_unpack_target(target);
285
- }
286
- }
287
- Node::OpAssign { target, op, object } => {
288
- let Some(opcode) = operator_to_inplace_opcode(op) else {
289
- return Err(CompileError::new(
290
- "matrix multiplication augmented assignment (@=) is not yet supported",
291
- target.position,
292
- ));
293
- };
294
- self.compile_name(target);
295
- self.compile_expr(object)?;
296
- self.code.emit(opcode);
297
- self.compile_store(target);
298
- }
299
- Node::SubscriptOpAssign {
300
- target,
301
- index,
302
- op,
303
- object,
304
- target_position,
305
- } => {
306
- let Some(opcode) = operator_to_inplace_opcode(op) else {
307
- return Err(CompileError::new(
308
- "matrix multiplication augmented assignment (@=) is not yet supported",
309
- *target_position,
310
- ));
311
- };
312
- self.compile_name(target);
313
- self.compile_expr(index)?;
314
- self.code.emit(Opcode::Dup2);
315
- self.code.set_location(*target_position, None);
316
- self.code.emit(Opcode::BinarySubscr);
317
- self.compile_expr(object)?;
318
- self.code.emit(opcode);
319
- self.code.emit(Opcode::Rot3);
320
- self.code.set_location(*target_position, None);
321
- self.code.emit(Opcode::StoreSubscr);
322
- }
323
- Node::SubscriptAssign {
324
- target,
325
- index,
326
- value,
327
- target_position,
328
- } => {
329
- // Stack order for StoreSubscr: value, obj, index
330
- self.compile_expr(value)?;
331
- self.compile_name(target);
332
- self.compile_expr(index)?;
333
- // Set location to the target (e.g., `lst[10]`) for proper caret in tracebacks
334
- self.code.set_location(*target_position, None);
335
- self.code.emit(Opcode::StoreSubscr);
336
- }
337
- Node::AttrAssign {
338
- object,
339
- attr,
340
- target_position,
341
- value,
342
- } => {
343
- // Stack order for StoreAttr: value, obj
344
- self.compile_expr(value)?;
345
- self.compile_expr(object)?;
346
- let name_id = attr.string_id().expect("StoreAttr requires interned attr name");
347
- // Set location to the target (e.g., `x.foo`) for proper caret in tracebacks
348
- self.code.set_location(*target_position, None);
349
- self.code.emit_u16(
350
- Opcode::StoreAttr,
351
- u16::try_from(name_id.index()).expect("name index exceeds u16"),
352
- );
353
- }
354
- Node::If { test, body, or_else } => self.compile_if(test, body, or_else)?,
355
- Node::For {
356
- target,
357
- iter,
358
- body,
359
- or_else,
360
- } => self.compile_for(target, iter, body, or_else)?,
361
- Node::While { test, body, or_else } => self.compile_while(test, body, or_else)?,
362
- Node::Assert { test, msg } => self.compile_assert(test, msg.as_ref())?,
363
- Node::Raise(expr) => {
364
- if let Some(exc) = expr {
365
- self.compile_expr(exc)?;
366
- self.code.emit(Opcode::Raise);
367
- } else {
368
- self.code.emit(Opcode::Reraise);
369
- }
370
- }
371
- Node::FunctionDef(func_def) => self.compile_function_def(func_def)?,
372
- Node::Try(try_block) => self.compile_try(try_block)?,
373
- Node::Import { module_name, binding } => self.compile_import(*module_name, binding),
374
- Node::ImportFrom {
375
- module_name,
376
- names,
377
- position,
378
- } => self.compile_import_from(*module_name, names, *position),
379
- Node::Break { position } => self.compile_break(*position)?,
380
- Node::Continue { position } => self.compile_continue(*position)?,
381
- // These are handled during the prepare phase and produce no bytecode
382
- Node::Pass | Node::Global { .. } | Node::Nonlocal { .. } => {}
383
- }
384
- Ok(())
385
- }
386
-
387
- /// Compiles a function definition.
388
- ///
389
- /// This involves:
390
- /// 1. Recursively compiling the function body to bytecode
391
- /// 2. Creating a Function struct with the compiled Code
392
- /// 3. Adding the Function to the compiler's functions vector
393
- /// 4. Emitting bytecode to evaluate defaults and create the function at runtime
394
- fn compile_function_def(&mut self, func_def: &PreparedFunctionDef) -> Result<(), CompileError> {
395
- let func_pos = func_def.name.position;
396
-
397
- // Check bytecode operand limits
398
- if func_def.default_exprs.len() > MAX_CALL_ARGS {
399
- return Err(CompileError::new(
400
- format!("more than {MAX_CALL_ARGS} default parameter values"),
401
- func_pos,
402
- ));
403
- }
404
- if func_def.free_var_enclosing_slots.len() > MAX_CALL_ARGS {
405
- return Err(CompileError::new(
406
- format!("more than {MAX_CALL_ARGS} closure variables"),
407
- func_pos,
408
- ));
409
- }
410
-
411
- // 1. Compile the function body recursively
412
- // Take ownership of functions for the recursive compile, then restore
413
- let functions = std::mem::take(&mut self.functions);
414
- let namespace_size = u16::try_from(func_def.namespace_size).expect("function namespace size exceeds u16");
415
- let (body_code, mut functions) =
416
- Self::compile_function_body(&func_def.body, self.interns, functions, namespace_size)?;
417
-
418
- // 2. Create the compiled Function and add to the vector
419
- let func_id = functions.len();
420
- let function = Function::new(
421
- func_def.name,
422
- func_def.signature.clone(),
423
- func_def.namespace_size,
424
- func_def.free_var_enclosing_slots.clone(),
425
- func_def.cell_var_count,
426
- func_def.cell_param_indices.clone(),
427
- func_def.default_exprs.len(),
428
- func_def.is_async,
429
- body_code,
430
- );
431
- functions.push(function);
432
-
433
- // Restore functions to self
434
- self.functions = functions;
435
-
436
- // 3. Compile and push default values (evaluated at definition time)
437
- for default_expr in &func_def.default_exprs {
438
- self.compile_expr(default_expr)?;
439
- }
440
- let defaults_count =
441
- u8::try_from(func_def.default_exprs.len()).expect("function default argument count exceeds u8");
442
- let func_id_u16 = u16::try_from(func_id).expect("function count exceeds u16");
443
-
444
- // 4. Emit MakeFunction or MakeClosure (if has free vars)
445
- if func_def.free_var_enclosing_slots.is_empty() {
446
- // MakeFunction: func_id (u16) + defaults_count (u8)
447
- self.code.emit_u16_u8(Opcode::MakeFunction, func_id_u16, defaults_count);
448
- } else {
449
- // Push captured cells from enclosing scope
450
- for &slot in &func_def.free_var_enclosing_slots {
451
- // Load the cell reference from the enclosing namespace
452
- let slot_u16 = u16::try_from(slot.index()).expect("closure slot index exceeds u16");
453
- self.code.emit_load_local(slot_u16);
454
- }
455
- let cell_count =
456
- u8::try_from(func_def.free_var_enclosing_slots.len()).expect("closure cell count exceeds u8");
457
- // MakeClosure: func_id (u16) + defaults_count (u8) + cell_count (u8)
458
- self.code
459
- .emit_u16_u8_u8(Opcode::MakeClosure, func_id_u16, defaults_count, cell_count);
460
- }
461
-
462
- // 5. Store the function object to its name slot
463
- self.compile_store(&func_def.name);
464
-
465
- Ok(())
466
- }
467
-
468
- /// Compiles a lambda expression.
469
- ///
470
- /// This is similar to `compile_function_def` but:
471
- /// - Does NOT store the function to a name slot (it stays on the stack as an expression result)
472
- ///
473
- /// The lambda's `PreparedFunctionDef` already has `<lambda>` as its name.
474
- fn compile_lambda(&mut self, func_def: &PreparedFunctionDef) -> Result<(), CompileError> {
475
- let func_pos = func_def.name.position;
476
-
477
- // Check bytecode operand limits
478
- if func_def.default_exprs.len() > MAX_CALL_ARGS {
479
- return Err(CompileError::new(
480
- format!("more than {MAX_CALL_ARGS} default parameter values"),
481
- func_pos,
482
- ));
483
- }
484
- if func_def.free_var_enclosing_slots.len() > MAX_CALL_ARGS {
485
- return Err(CompileError::new(
486
- format!("more than {MAX_CALL_ARGS} closure variables"),
487
- func_pos,
488
- ));
489
- }
490
-
491
- // 1. Compile the function body recursively
492
- let functions = std::mem::take(&mut self.functions);
493
- let namespace_size = u16::try_from(func_def.namespace_size).expect("function namespace size exceeds u16");
494
- let (body_code, mut functions) =
495
- Self::compile_function_body(&func_def.body, self.interns, functions, namespace_size)?;
496
-
497
- // 2. Create the compiled Function and add to the vector
498
- let func_id = functions.len();
499
- let function = Function::new(
500
- func_def.name,
501
- func_def.signature.clone(),
502
- func_def.namespace_size,
503
- func_def.free_var_enclosing_slots.clone(),
504
- func_def.cell_var_count,
505
- func_def.cell_param_indices.clone(),
506
- func_def.default_exprs.len(),
507
- func_def.is_async,
508
- body_code,
509
- );
510
- functions.push(function);
511
-
512
- // Restore functions to self
513
- self.functions = functions;
514
-
515
- // 3. Compile and push default values (evaluated at definition time)
516
- for default_expr in &func_def.default_exprs {
517
- self.compile_expr(default_expr)?;
518
- }
519
- let defaults_count =
520
- u8::try_from(func_def.default_exprs.len()).expect("function default argument count exceeds u8");
521
- let func_id_u16 = u16::try_from(func_id).expect("function count exceeds u16");
522
-
523
- // 4. Emit MakeFunction or MakeClosure (if has free vars)
524
- if func_def.free_var_enclosing_slots.is_empty() {
525
- // MakeFunction: func_id (u16) + defaults_count (u8)
526
- self.code.emit_u16_u8(Opcode::MakeFunction, func_id_u16, defaults_count);
527
- } else {
528
- // Push captured cells from enclosing scope
529
- for &slot in &func_def.free_var_enclosing_slots {
530
- let slot_u16 = u16::try_from(slot.index()).expect("closure slot index exceeds u16");
531
- self.code.emit_load_local(slot_u16);
532
- }
533
- let cell_count =
534
- u8::try_from(func_def.free_var_enclosing_slots.len()).expect("closure cell count exceeds u8");
535
- // MakeClosure: func_id (u16) + defaults_count (u8) + cell_count (u8)
536
- self.code
537
- .emit_u16_u8_u8(Opcode::MakeClosure, func_id_u16, defaults_count, cell_count);
538
- }
539
-
540
- // NOTE: Unlike compile_function_def, we do NOT call compile_store here.
541
- // The function object stays on the stack as an expression result.
542
-
543
- Ok(())
544
- }
545
-
546
- /// Compiles an import statement.
547
- ///
548
- /// Emits `LoadModule` to create the module, then stores it to the binding name.
549
- /// If the module is unknown, emits `RaiseImportError` to defer the error to runtime.
550
- /// This allows imports inside `if TYPE_CHECKING:` blocks to compile successfully.
551
- fn compile_import(&mut self, module_name: StringId, binding: &Identifier) {
552
- let position = binding.position;
553
- self.code.set_location(position, None);
554
-
555
- // Look up the module by name
556
- if let Some(builtin_module) = BuiltinModule::from_string_id(module_name) {
557
- // Known module - emit LoadModule
558
- self.code.emit_u8(Opcode::LoadModule, builtin_module as u8);
559
- // Store to the binding (respects Local/Global/Cell scope)
560
- self.compile_store(binding);
561
- } else {
562
- // Unknown module - defer error to runtime with RaiseImportError
563
- // This allows TYPE_CHECKING imports to compile without error
564
- let name_const = self.code.add_const(Value::InternString(module_name));
565
- self.code.emit_u16(Opcode::RaiseImportError, name_const);
566
- }
567
- }
568
-
569
- /// Compiles a `from module import name, ...` statement.
570
- ///
571
- /// Creates the module once, then loads each attribute and stores to the binding.
572
- /// Invalid attribute names will raise `AttributeError` at runtime.
573
- /// If the module is unknown, emits `RaiseImportError` to defer the error to runtime.
574
- /// This allows imports inside `if TYPE_CHECKING:` blocks to compile successfully.
575
- fn compile_import_from(&mut self, module_name: StringId, names: &[(StringId, Identifier)], position: CodeRange) {
576
- self.code.set_location(position, None);
577
-
578
- // Look up the module
579
- if let Some(builtin_module) = BuiltinModule::from_string_id(module_name) {
580
- // Known module - emit LoadModule
581
- self.code.emit_u8(Opcode::LoadModule, builtin_module as u8);
582
-
583
- // For each name to import
584
- for (i, (import_name, binding)) in names.iter().enumerate() {
585
- // Dup the module if this isn't the last import (last one consumes the module)
586
- if i < names.len() - 1 {
587
- self.code.emit(Opcode::Dup);
588
- }
589
-
590
- // Load the attribute from the module (raises ImportError if not found)
591
- let name_idx = u16::try_from(import_name.index()).expect("name index exceeds u16");
592
- self.code.emit_u16(Opcode::LoadAttrImport, name_idx);
593
-
594
- // Store to the binding
595
- self.compile_store(binding);
596
- }
597
- } else {
598
- // Unknown module - defer error to runtime with RaiseImportError
599
- // This allows TYPE_CHECKING imports to compile without error
600
- let name_const = self.code.add_const(Value::InternString(module_name));
601
- self.code.emit_u16(Opcode::RaiseImportError, name_const);
602
- }
603
- }
604
-
605
- // ========================================================================
606
- // Expression Compilation
607
- // ========================================================================
608
-
609
- /// Compiles an expression, leaving its value on the stack.
610
- fn compile_expr(&mut self, expr_loc: &ExprLoc) -> Result<(), CompileError> {
611
- // Set source location for traceback info
612
- self.code.set_location(expr_loc.position, None);
613
-
614
- match &expr_loc.expr {
615
- Expr::Literal(lit) => self.compile_literal(lit),
616
-
617
- Expr::Name(ident) => self.compile_name(ident),
618
-
619
- Expr::Builtin(builtin) => {
620
- let idx = self.code.add_const(Value::Builtin(*builtin));
621
- self.code.emit_u16(Opcode::LoadConst, idx);
622
- }
623
-
624
- Expr::Op { left, op, right } => {
625
- self.compile_binary_op(left, op, right, expr_loc.position)?;
626
- }
627
-
628
- Expr::CmpOp { left, op, right } => {
629
- self.compile_expr(left)?;
630
- self.compile_expr(right)?;
631
- // Restore the full comparison expression's position for traceback caret range
632
- self.code.set_location(expr_loc.position, None);
633
- // ModEq needs special handling - it has a constant operand
634
- if let CmpOperator::ModEq(value) = op {
635
- let const_idx = self.code.add_const(Value::Int(*value));
636
- self.code.emit_u16(Opcode::CompareModEq, const_idx);
637
- } else {
638
- self.code.emit(cmp_operator_to_opcode(op));
639
- }
640
- }
641
-
642
- Expr::ChainCmp { left, comparisons } => {
643
- self.compile_chain_comparison(left, comparisons, expr_loc.position)?;
644
- }
645
-
646
- Expr::Not(operand) => {
647
- self.compile_expr(operand)?;
648
- // Restore the full expression's position for traceback caret range
649
- self.code.set_location(expr_loc.position, None);
650
- self.code.emit(Opcode::UnaryNot);
651
- }
652
-
653
- Expr::UnaryMinus(operand) => {
654
- self.compile_expr(operand)?;
655
- // Restore the full expression's position for traceback caret range
656
- self.code.set_location(expr_loc.position, None);
657
- self.code.emit(Opcode::UnaryNeg);
658
- }
659
-
660
- Expr::UnaryPlus(operand) => {
661
- self.compile_expr(operand)?;
662
- // Restore the full expression's position for traceback caret range
663
- self.code.set_location(expr_loc.position, None);
664
- self.code.emit(Opcode::UnaryPos);
665
- }
666
-
667
- Expr::UnaryInvert(operand) => {
668
- self.compile_expr(operand)?;
669
- // Restore the full expression's position for traceback caret range
670
- self.code.set_location(expr_loc.position, None);
671
- self.code.emit(Opcode::UnaryInvert);
672
- }
673
-
674
- Expr::List(elements) => {
675
- if has_unpack_seq(elements) {
676
- // Generalized path: build incrementally for PEP 448 *unpacks
677
- self.code.emit_u16(Opcode::BuildList, 0);
678
- for item in elements {
679
- match item {
680
- SequenceItem::Value(e) => {
681
- self.compile_expr(e)?;
682
- self.code.emit_u8(Opcode::ListAppend, 0);
683
- }
684
- SequenceItem::Unpack(e) => {
685
- self.compile_expr(e)?;
686
- self.code.emit(Opcode::ListExtend);
687
- }
688
- }
689
- }
690
- } else {
691
- // Fast path: all values, single BuildList.
692
- // SAFETY: has_unpack_seq(elements) is false, so every item is Value.
693
- for item in elements {
694
- let SequenceItem::Value(e) = item else {
695
- unreachable!("list fast path: only Value items")
696
- };
697
- self.compile_expr(e)?;
698
- }
699
- self.code.emit_u16(
700
- Opcode::BuildList,
701
- u16::try_from(elements.len()).expect("elements count exceeds u16"),
702
- );
703
- }
704
- }
705
-
706
- Expr::Tuple(elements) => {
707
- if has_unpack_seq(elements) {
708
- // Generalized path: build via list then convert for PEP 448 *unpacks
709
- self.code.emit_u16(Opcode::BuildList, 0);
710
- for item in elements {
711
- match item {
712
- SequenceItem::Value(e) => {
713
- self.compile_expr(e)?;
714
- self.code.emit_u8(Opcode::ListAppend, 0);
715
- }
716
- SequenceItem::Unpack(e) => {
717
- self.compile_expr(e)?;
718
- self.code.emit(Opcode::ListExtend);
719
- }
720
- }
721
- }
722
- self.code.emit(Opcode::ListToTuple);
723
- } else {
724
- // Fast path: all values, single BuildTuple.
725
- // SAFETY: has_unpack_seq(elements) is false, so every item is Value.
726
- for item in elements {
727
- let SequenceItem::Value(e) = item else {
728
- unreachable!("tuple fast path: only Value items")
729
- };
730
- self.compile_expr(e)?;
731
- }
732
- self.code.emit_u16(
733
- Opcode::BuildTuple,
734
- u16::try_from(elements.len()).expect("elements count exceeds u16"),
735
- );
736
- }
737
- }
738
-
739
- Expr::Dict(dict_items) => {
740
- if has_unpack_dict(dict_items) {
741
- // Generalized path: build incrementally for PEP 448 **unpacks
742
- self.code.emit_u16(Opcode::BuildDict, 0);
743
- for item in dict_items {
744
- match item {
745
- DictItem::Pair(key, value) => {
746
- self.compile_expr(key)?;
747
- self.compile_expr(value)?;
748
- // depth=0: dict is at TOS after key/value are popped
749
- self.code.emit_u8(Opcode::DictSetItem, 0);
750
- }
751
- DictItem::Unpack(e) => {
752
- self.compile_expr(e)?;
753
- // depth=0: dict is directly below mapping on stack
754
- self.code.emit_u8(Opcode::DictUpdate, 0);
755
- }
756
- }
757
- }
758
- } else {
759
- // Fast path: all pairs, single BuildDict.
760
- // SAFETY: has_unpack_dict(dict_items) is false, so every item is Pair.
761
- for item in dict_items {
762
- let DictItem::Pair(key, value) = item else {
763
- unreachable!("dict fast path: only Pair items")
764
- };
765
- self.compile_expr(key)?;
766
- self.compile_expr(value)?;
767
- }
768
- self.code.emit_u16(
769
- Opcode::BuildDict,
770
- u16::try_from(dict_items.len()).expect("pairs count exceeds u16"),
771
- );
772
- }
773
- }
774
-
775
- Expr::Set(elements) => {
776
- if has_unpack_seq(elements) {
777
- // Generalized path: build incrementally for PEP 448 *unpacks
778
- self.code.emit_u16(Opcode::BuildSet, 0);
779
- for item in elements {
780
- match item {
781
- SequenceItem::Value(e) => {
782
- self.compile_expr(e)?;
783
- self.code.emit_u8(Opcode::SetAdd, 0);
784
- }
785
- SequenceItem::Unpack(e) => {
786
- self.compile_expr(e)?;
787
- self.code.emit_u8(Opcode::SetExtend, 0);
788
- }
789
- }
790
- }
791
- } else {
792
- // Fast path: all values, single BuildSet.
793
- // SAFETY: has_unpack_seq(elements) is false, so every item is Value.
794
- for item in elements {
795
- let SequenceItem::Value(e) = item else {
796
- unreachable!("set fast path: only Value items")
797
- };
798
- self.compile_expr(e)?;
799
- }
800
- self.code.emit_u16(
801
- Opcode::BuildSet,
802
- u16::try_from(elements.len()).expect("elements count exceeds u16"),
803
- );
804
- }
805
- }
806
-
807
- Expr::Subscript { object, index } => {
808
- self.compile_expr(object)?;
809
- self.compile_expr(index)?;
810
- // Restore the full subscript expression's position for traceback
811
- self.code.set_location(expr_loc.position, None);
812
- self.code.emit(Opcode::BinarySubscr);
813
- }
814
-
815
- Expr::IfElse { test, body, orelse } => {
816
- self.compile_if_else_expr(test, body, orelse)?;
817
- }
818
-
819
- Expr::AttrGet { object, attr } => {
820
- self.compile_expr(object)?;
821
- // Restore the full expression's position for traceback caret range
822
- self.code.set_location(expr_loc.position, None);
823
- let name_id = attr.string_id().expect("LoadAttr requires interned attr name");
824
- self.code.emit_u16(
825
- Opcode::LoadAttr,
826
- u16::try_from(name_id.index()).expect("name index exceeds u16"),
827
- );
828
- }
829
-
830
- Expr::Call { callable, args } => {
831
- self.compile_call(callable, args, expr_loc.position)?;
832
- }
833
-
834
- Expr::AttrCall { object, attr, args } => {
835
- // Compile the object (will be on the stack)
836
- self.compile_expr(object)?;
837
-
838
- // Compile the attribute call arguments and emit CallAttr
839
- self.compile_method_call(attr, args, expr_loc.position)?;
840
- }
841
-
842
- Expr::IndirectCall { callable, args } => {
843
- // Compile the callable expression (e.g., a lambda)
844
- self.compile_expr(callable)?;
845
-
846
- // Compile arguments and emit the call
847
- self.compile_call_args(args, expr_loc.position)?;
848
- }
849
-
850
- Expr::FString(parts) => {
851
- // Compile each part and build the f-string
852
- let part_count = self.compile_fstring_parts(parts)?;
853
- self.code.emit_u16(Opcode::BuildFString, part_count);
854
- }
855
-
856
- Expr::ListComp { elt, generators } => {
857
- self.compile_list_comp(elt, generators)?;
858
- }
859
-
860
- Expr::SetComp { elt, generators } => {
861
- self.compile_set_comp(elt, generators)?;
862
- }
863
-
864
- Expr::DictComp { key, value, generators } => {
865
- self.compile_dict_comp(key, value, generators)?;
866
- }
867
-
868
- Expr::Lambda { func_def } => {
869
- self.compile_lambda(func_def)?;
870
- }
871
-
872
- Expr::LambdaRaw { .. } => {
873
- // LambdaRaw should be converted to Lambda during prepare phase
874
- unreachable!("Expr::LambdaRaw should not exist after prepare phase")
875
- }
876
-
877
- Expr::Await(value) => {
878
- // Await expressions: compile the inner expression, then emit Await
879
- // Await handles ExternalFuture, Coroutine, and GatherFuture
880
- self.compile_expr(value)?;
881
- // Restore the full expression's position for traceback caret range
882
- self.code.set_location(expr_loc.position, None);
883
- self.code.emit(Opcode::Await);
884
- }
885
-
886
- Expr::Slice { lower, upper, step } => {
887
- // Compile slice components: start, stop, step (push None for missing)
888
- if let Some(lower) = lower {
889
- self.compile_expr(lower)?;
890
- } else {
891
- self.code.emit(Opcode::LoadNone);
892
- }
893
- if let Some(upper) = upper {
894
- self.compile_expr(upper)?;
895
- } else {
896
- self.code.emit(Opcode::LoadNone);
897
- }
898
- if let Some(step) = step {
899
- self.compile_expr(step)?;
900
- } else {
901
- self.code.emit(Opcode::LoadNone);
902
- }
903
- self.code.emit(Opcode::BuildSlice);
904
- }
905
-
906
- Expr::Named { target, value } => {
907
- // Compile the value expression (leaves result on stack)
908
- self.compile_expr(value)?;
909
- // Duplicate so value remains after store
910
- self.code.emit(Opcode::Dup);
911
- // Store to target (pops one copy)
912
- self.compile_store(target);
913
- }
914
- }
915
- Ok(())
916
- }
917
-
918
- // ========================================================================
919
- // Literal Compilation
920
- // ========================================================================
921
-
922
- /// Compiles a literal value.
923
- fn compile_literal(&mut self, literal: &Literal) {
924
- match literal {
925
- Literal::None => {
926
- self.code.emit(Opcode::LoadNone);
927
- }
928
-
929
- Literal::Bool(true) => {
930
- self.code.emit(Opcode::LoadTrue);
931
- }
932
-
933
- Literal::Bool(false) => {
934
- self.code.emit(Opcode::LoadFalse);
935
- }
936
-
937
- Literal::Int(n) => {
938
- // Use LoadSmallInt for values that fit in i8
939
- if let Ok(small) = i8::try_from(*n) {
940
- self.code.emit_i8(Opcode::LoadSmallInt, small);
941
- } else {
942
- let idx = self.code.add_const(Value::from(*literal));
943
- self.code.emit_u16(Opcode::LoadConst, idx);
944
- }
945
- }
946
-
947
- // For Float, Str, Bytes, Ellipsis - use LoadConst with Value::from
948
- _ => {
949
- let idx = self.code.add_const(Value::from(*literal));
950
- self.code.emit_u16(Opcode::LoadConst, idx);
951
- }
952
- }
953
- }
954
-
955
- // ========================================================================
956
- // Variable Operations
957
- // ========================================================================
958
-
959
- /// Compiles loading a variable onto the stack.
960
- ///
961
- /// At module level, `Local` and `LocalUnassigned` scopes emit global opcodes
962
- /// because module-level locals live in the globals array.
963
- fn compile_name(&mut self, ident: &Identifier) {
964
- let slot = u16::try_from(ident.namespace_id().index()).expect("local slot exceeds u16");
965
- match ident.scope {
966
- NameScope::Local => {
967
- // True local - register name and mark as assigned for UnboundLocalError
968
- self.code.register_local_name(slot, ident.name_id);
969
- self.code.register_assigned_local(slot);
970
- if self.is_module_scope {
971
- self.code.emit_u16(Opcode::LoadGlobal, slot);
972
- } else {
973
- self.code.emit_load_local(slot);
974
- }
975
- }
976
- NameScope::LocalUnassigned => {
977
- // Undefined reference - register name but NOT as assigned for NameError
978
- self.code.register_local_name(slot, ident.name_id);
979
- if self.is_module_scope {
980
- self.code.emit_u16(Opcode::LoadGlobal, slot);
981
- } else {
982
- self.code.emit_load_local(slot);
983
- }
984
- }
985
- NameScope::Global => {
986
- // Register the name for NameError/NameLookup messages
987
- self.code.register_local_name(slot, ident.name_id);
988
- self.code.emit_u16(Opcode::LoadGlobal, slot);
989
- }
990
- NameScope::Cell => {
991
- // Register the name for NameError messages (unbound free variable)
992
- self.code.register_local_name(slot, ident.name_id);
993
- // Emit local slot index — the VM reads the cell HeapId from the stack
994
- self.code.emit_u16(Opcode::LoadCell, slot);
995
- }
996
- }
997
- }
998
-
999
- /// Compiles loading a variable in call context (e.g., `foo()` loads `foo`).
1000
- ///
1001
- /// For `LocalUnassigned` and `Global` scopes, emits callable-aware load opcodes
1002
- /// that push `ExtFunction(name_id)` for undefined names instead of yielding
1003
- /// `NameLookup`. This allows execution to reach `CallFunction`, which naturally
1004
- /// yields `FunctionCall` — giving the host a chance to handle external function calls.
1005
- ///
1006
- /// For `Local` and `Cell` scopes, delegates to `compile_name` since those can't
1007
- /// be external functions (they're always defined locally or captured).
1008
- fn compile_name_callable(&mut self, ident: &Identifier) {
1009
- let slot = u16::try_from(ident.namespace_id().index()).expect("local slot exceeds u16");
1010
- match ident.scope {
1011
- NameScope::LocalUnassigned => {
1012
- // Undefined reference in call context - use callable-aware load.
1013
- // At module level, use global callable since locals are in the globals array.
1014
- self.code.register_local_name(slot, ident.name_id);
1015
- if self.is_module_scope {
1016
- self.code.emit_load_global_callable(slot, ident.name_id);
1017
- } else {
1018
- self.code.emit_load_local_callable(slot, ident.name_id);
1019
- }
1020
- }
1021
- NameScope::Global => {
1022
- // Global scope - name_id is encoded in the operand because global slot
1023
- // indices are in a different namespace from local slots, so looking up
1024
- // the name from the current frame's local_names would be incorrect
1025
- self.code.emit_load_global_callable(slot, ident.name_id);
1026
- }
1027
- // Local and Cell can't be external functions - use regular load
1028
- NameScope::Local | NameScope::Cell => self.compile_name(ident),
1029
- }
1030
- }
1031
-
1032
- /// Compiles storing the top of stack to a variable.
1033
- ///
1034
- /// At module level, `Local` and `LocalUnassigned` scopes emit `StoreGlobal`
1035
- /// because module-level locals live in the globals array.
1036
- fn compile_store(&mut self, target: &Identifier) {
1037
- let slot = u16::try_from(target.namespace_id().index()).expect("local slot exceeds u16");
1038
- match target.scope {
1039
- NameScope::Local | NameScope::LocalUnassigned => {
1040
- self.code.register_local_name(slot, target.name_id);
1041
- if self.is_module_scope {
1042
- self.code.emit_u16(Opcode::StoreGlobal, slot);
1043
- } else {
1044
- self.code.emit_store_local(slot);
1045
- }
1046
- }
1047
- NameScope::Global => {
1048
- self.code.emit_u16(Opcode::StoreGlobal, slot);
1049
- }
1050
- NameScope::Cell => {
1051
- // Emit local slot index — the VM reads the cell HeapId from the stack
1052
- self.code.emit_u16(Opcode::StoreCell, slot);
1053
- }
1054
- }
1055
- }
1056
-
1057
- // ========================================================================
1058
- // Binary Operator Compilation
1059
- // ========================================================================
1060
-
1061
- /// Compiles a binary operation.
1062
- ///
1063
- /// `parent_pos` is the position of the full binary expression (e.g., `1 / 0`),
1064
- /// which we restore before emitting the opcode so tracebacks show the right range.
1065
- fn compile_binary_op(
1066
- &mut self,
1067
- left: &ExprLoc,
1068
- op: &Operator,
1069
- right: &ExprLoc,
1070
- parent_pos: CodeRange,
1071
- ) -> Result<(), CompileError> {
1072
- match op {
1073
- // Short-circuit AND: evaluate left, jump if falsy
1074
- Operator::And => {
1075
- self.compile_expr(left)?;
1076
- let end_jump = self.code.emit_jump(Opcode::JumpIfFalseOrPop);
1077
- self.compile_expr(right)?;
1078
- self.code.patch_jump(end_jump);
1079
- }
1080
-
1081
- // Short-circuit OR: evaluate left, jump if truthy
1082
- Operator::Or => {
1083
- self.compile_expr(left)?;
1084
- let end_jump = self.code.emit_jump(Opcode::JumpIfTrueOrPop);
1085
- self.compile_expr(right)?;
1086
- self.code.patch_jump(end_jump);
1087
- }
1088
-
1089
- // Regular binary operators
1090
- _ => {
1091
- self.compile_expr(left)?;
1092
- self.compile_expr(right)?;
1093
- // Restore the full expression's position for traceback caret range
1094
- self.code.set_location(parent_pos, None);
1095
- self.code.emit(operator_to_opcode(op));
1096
- }
1097
- }
1098
- Ok(())
1099
- }
1100
-
1101
- /// Compiles a chain comparison expression like `a < b < c < d`.
1102
- ///
1103
- /// Chain comparisons evaluate each intermediate value only once and short-circuit
1104
- /// on the first false result. Uses stack manipulation to avoid namespace pollution.
1105
- ///
1106
- /// Bytecode strategy for `a < b < c`:
1107
- /// ```text
1108
- /// eval a # Stack: [a]
1109
- /// eval b # Stack: [a, b]
1110
- /// Dup # Stack: [a, b, b]
1111
- /// Rot3 # Stack: [b, a, b]
1112
- /// CompareLt # Stack: [b, result1]
1113
- /// JumpIfFalseOrPop # if false: jump to cleanup; if true: pop, stack=[b]
1114
- /// eval c # Stack: [b, c]
1115
- /// CompareLt # Stack: [result2]
1116
- /// Jump @end
1117
- /// @cleanup: # Stack: [b, False]
1118
- /// Rot2 # Stack: [False, b]
1119
- /// Pop # Stack: [False]
1120
- /// @end:
1121
- /// ```
1122
- fn compile_chain_comparison(
1123
- &mut self,
1124
- left: &ExprLoc,
1125
- comparisons: &[(CmpOperator, ExprLoc)],
1126
- position: CodeRange,
1127
- ) -> Result<(), CompileError> {
1128
- let n = comparisons.len();
1129
-
1130
- // Remember stack depth before the chain for cleanup calculation
1131
- let base_depth = self.code.stack_depth();
1132
-
1133
- // Compile leftmost operand
1134
- self.compile_expr(left)?;
1135
-
1136
- // Track jump targets for short-circuit cleanup
1137
- let mut cleanup_jumps = Vec::with_capacity(n - 1);
1138
-
1139
- for (i, (op, right)) in comparisons.iter().enumerate() {
1140
- let is_last = i == n - 1;
1141
-
1142
- // Compile the right operand
1143
- self.compile_expr(right)?;
1144
-
1145
- if !is_last {
1146
- // Keep a copy of the intermediate for the next comparison
1147
- self.code.emit(Opcode::Dup);
1148
- // Reorder: [prev, curr, curr] -> [curr, prev, curr]
1149
- self.code.emit(Opcode::Rot3);
1150
- }
1151
-
1152
- // Emit comparison
1153
- self.code.set_location(position, None);
1154
- if let CmpOperator::ModEq(value) = op {
1155
- let const_idx = self.code.add_const(Value::Int(*value));
1156
- self.code.emit_u16(Opcode::CompareModEq, const_idx);
1157
- } else {
1158
- self.code.emit(cmp_operator_to_opcode(op));
1159
- }
1160
-
1161
- if !is_last {
1162
- // Short-circuit: if false, jump to cleanup
1163
- let jump = self.code.emit_jump(Opcode::JumpIfFalseOrPop);
1164
- cleanup_jumps.push(jump);
1165
- }
1166
- }
1167
-
1168
- // Jump past cleanup (result already on stack)
1169
- let end_jump = self.code.emit_jump(Opcode::Jump);
1170
-
1171
- // Cleanup: remove the saved intermediate value, keep False result
1172
- // The cleanup is only reached via JumpIfFalseOrPop which doesn't pop,
1173
- // so the stack has: [intermediate, False] (2 extra items from base)
1174
- for jump in cleanup_jumps {
1175
- self.code.patch_jump(jump);
1176
- }
1177
- self.code.set_stack_depth(base_depth + 2); // [intermediate, False]
1178
- self.code.emit(Opcode::Rot2); // [False, intermediate]
1179
- self.code.emit(Opcode::Pop); // [False]
1180
-
1181
- self.code.patch_jump(end_jump);
1182
- // Final result is on stack: base_depth + 1
1183
- self.code.set_stack_depth(base_depth + 1);
1184
-
1185
- Ok(())
1186
- }
1187
-
1188
- // ========================================================================
1189
- // Control Flow Compilation
1190
- // ========================================================================
1191
-
1192
- /// Compiles an if/else statement.
1193
- fn compile_if(
1194
- &mut self,
1195
- test: &ExprLoc,
1196
- body: &[PreparedNode],
1197
- or_else: &[PreparedNode],
1198
- ) -> Result<(), CompileError> {
1199
- self.compile_expr(test)?;
1200
-
1201
- if or_else.is_empty() {
1202
- // Simple if without else
1203
- let end_jump = self.code.emit_jump(Opcode::JumpIfFalse);
1204
- self.compile_block(body)?;
1205
- self.code.patch_jump(end_jump);
1206
- } else {
1207
- // If with else
1208
- let else_jump = self.code.emit_jump(Opcode::JumpIfFalse);
1209
- self.compile_block(body)?;
1210
- let end_jump = self.code.emit_jump(Opcode::Jump);
1211
- self.code.patch_jump(else_jump);
1212
- self.compile_block(or_else)?;
1213
- self.code.patch_jump(end_jump);
1214
- }
1215
- Ok(())
1216
- }
1217
-
1218
- /// Compiles a ternary conditional expression.
1219
- fn compile_if_else_expr(&mut self, test: &ExprLoc, body: &ExprLoc, orelse: &ExprLoc) -> Result<(), CompileError> {
1220
- self.compile_expr(test)?;
1221
- let else_jump = self.code.emit_jump(Opcode::JumpIfFalse);
1222
- self.compile_expr(body)?;
1223
- let end_jump = self.code.emit_jump(Opcode::Jump);
1224
- self.code.patch_jump(else_jump);
1225
- self.compile_expr(orelse)?;
1226
- self.code.patch_jump(end_jump);
1227
- Ok(())
1228
- }
1229
-
1230
- /// Compiles a function call expression.
1231
- ///
1232
- /// For builtin calls with positional-only arguments, emits the optimized `CallBuiltin`
1233
- /// opcode which avoids pushing/popping the callable on the stack.
1234
- ///
1235
- /// For other calls, pushes the callable onto the stack, then all arguments, then emits
1236
- /// `CallFunction` or `CallFunctionKw`.
1237
- ///
1238
- /// The `call_pos` is the position of the full call expression for proper traceback caret.
1239
- fn compile_call(&mut self, callable: &Callable, args: &ArgExprs, call_pos: CodeRange) -> Result<(), CompileError> {
1240
- // Check if we can use the optimized CallBuiltinFunction path:
1241
- // - Callable must be a builtin function (known at compile time)
1242
- // - Arguments must be positional-only (Empty, One, Two, or Args)
1243
- if let Callable::Builtin(Builtins::Function(builtin_func)) = callable
1244
- && let Some(arg_count) = self.compile_builtin_call(args, call_pos)?
1245
- {
1246
- // Optimization applied - CallBuiltinFunction emitted
1247
- self.code.set_location(call_pos, None);
1248
- self.code.emit_call_builtin_function(*builtin_func as u8, arg_count);
1249
- return Ok(());
1250
- }
1251
- // Fall through to standard path for kwargs/unpacking
1252
-
1253
- // Check if we can use the optimized CallBuiltinType path:
1254
- // - Callable must be a builtin type constructor (known at compile time)
1255
- // - Arguments must be positional-only (Empty, One, Two, or Args)
1256
- if let Callable::Builtin(Builtins::Type(t)) = callable
1257
- && let Some(type_id) = t.callable_to_u8()
1258
- && let Some(arg_count) = self.compile_builtin_call(args, call_pos)?
1259
- {
1260
- // Optimization applied - CallBuiltinType emitted
1261
- self.code.set_location(call_pos, None);
1262
- self.code.emit_call_builtin_type(type_id, arg_count);
1263
- return Ok(());
1264
- }
1265
- // Fall through to standard path for kwargs/unpacking or non-callable types
1266
-
1267
- // Standard path: push callable, compile args, emit CallFunction/CallFunctionKw
1268
- // Push the callable (use name position for NameError caret range)
1269
- match callable {
1270
- Callable::Builtin(builtin) => {
1271
- let idx = self.code.add_const(Value::Builtin(*builtin));
1272
- self.code.emit_u16(Opcode::LoadConst, idx);
1273
- }
1274
- Callable::Name(ident) => {
1275
- // Use callable-aware load opcodes so undefined names produce ExtFunction
1276
- // instead of yielding NameLookup, allowing CallFunction to yield FunctionCall
1277
- self.code.set_location(ident.position, None);
1278
- self.compile_name_callable(ident);
1279
- }
1280
- }
1281
-
1282
- // Compile arguments and emit the call
1283
- // Restore full call position before CallFunction for call-related errors
1284
- match args {
1285
- ArgExprs::Empty => {
1286
- self.code.set_location(call_pos, None);
1287
- self.code.emit_u8(Opcode::CallFunction, 0);
1288
- }
1289
- ArgExprs::One(arg) => {
1290
- self.compile_expr(arg)?;
1291
- self.code.set_location(call_pos, None);
1292
- self.code.emit_u8(Opcode::CallFunction, 1);
1293
- }
1294
- ArgExprs::Two(arg1, arg2) => {
1295
- self.compile_expr(arg1)?;
1296
- self.compile_expr(arg2)?;
1297
- self.code.set_location(call_pos, None);
1298
- self.code.emit_u8(Opcode::CallFunction, 2);
1299
- }
1300
- ArgExprs::Args(args) => {
1301
- // Check argument count limit before compiling
1302
- if args.len() > MAX_CALL_ARGS {
1303
- return Err(CompileError::new(
1304
- format!("more than {MAX_CALL_ARGS} positional arguments in function call"),
1305
- call_pos,
1306
- ));
1307
- }
1308
- for arg in args {
1309
- self.compile_expr(arg)?;
1310
- }
1311
- let arg_count = u8::try_from(args.len()).expect("argument count exceeds u8");
1312
- self.code.set_location(call_pos, None);
1313
- self.code.emit_u8(Opcode::CallFunction, arg_count);
1314
- }
1315
- ArgExprs::Kwargs(kwargs) => {
1316
- // Check keyword argument count limit
1317
- if kwargs.len() > MAX_CALL_ARGS {
1318
- return Err(CompileError::new(
1319
- format!("more than {MAX_CALL_ARGS} keyword arguments in function call"),
1320
- call_pos,
1321
- ));
1322
- }
1323
- // Keyword-only call: compile kwarg values and emit CallFunctionKw
1324
- let mut kwname_ids = Vec::with_capacity(kwargs.len());
1325
- for kwarg in kwargs {
1326
- self.compile_expr(&kwarg.value)?;
1327
- kwname_ids.push(u16::try_from(kwarg.key.name_id.index()).expect("name index exceeds u16"));
1328
- }
1329
- self.code.set_location(call_pos, None);
1330
- self.code.emit_call_function_kw(0, &kwname_ids);
1331
- }
1332
- ArgExprs::ArgsKargs {
1333
- args,
1334
- var_args,
1335
- kwargs,
1336
- var_kwargs,
1337
- } => {
1338
- // Mixed positional and keyword arguments - may include *args or **kwargs unpacking
1339
- if var_args.is_some() || var_kwargs.is_some() {
1340
- // Use CallFunctionEx for unpacking - no limit on this path since
1341
- // args are built into a tuple dynamically at runtime
1342
- self.compile_call_with_unpacking(
1343
- callable,
1344
- args.as_ref(),
1345
- var_args.as_ref(),
1346
- kwargs.as_ref(),
1347
- var_kwargs.as_ref(),
1348
- call_pos,
1349
- )?;
1350
- } else {
1351
- // No unpacking - use CallFunctionKw for efficiency
1352
- // Check limits before compiling
1353
- let pos_count = args.as_ref().map_or(0, Vec::len);
1354
- let kw_count = kwargs.as_ref().map_or(0, Vec::len);
1355
-
1356
- if pos_count > MAX_CALL_ARGS {
1357
- return Err(CompileError::new(
1358
- format!("more than {MAX_CALL_ARGS} positional arguments in function call"),
1359
- call_pos,
1360
- ));
1361
- }
1362
- if kw_count > MAX_CALL_ARGS {
1363
- return Err(CompileError::new(
1364
- format!("more than {MAX_CALL_ARGS} keyword arguments in function call"),
1365
- call_pos,
1366
- ));
1367
- }
1368
-
1369
- // Compile positional args
1370
- if let Some(args) = args {
1371
- for arg in args {
1372
- self.compile_expr(arg)?;
1373
- }
1374
- }
1375
-
1376
- // Compile kwarg values and collect names
1377
- let mut kwname_ids = Vec::new();
1378
- if let Some(kwargs) = kwargs {
1379
- for kwarg in kwargs {
1380
- self.compile_expr(&kwarg.value)?;
1381
- kwname_ids.push(u16::try_from(kwarg.key.name_id.index()).expect("name index exceeds u16"));
1382
- }
1383
- }
1384
-
1385
- self.code.set_location(call_pos, None);
1386
- self.code.emit_call_function_kw(
1387
- u8::try_from(pos_count).expect("positional arg count exceeds u8"),
1388
- &kwname_ids,
1389
- );
1390
- }
1391
- }
1392
- ArgExprs::GeneralizedCall { args, kwargs } => {
1393
- // PEP 448: generalized unpacking — multiple *args or **kwargs.
1394
- // Callable was already pushed above this match; delegate to the helper.
1395
- let func_name_id = match callable {
1396
- Callable::Name(ident) => u16::try_from(ident.name_id.index()).expect("name index exceeds u16"),
1397
- Callable::Builtin(_) => 0xFFFF,
1398
- };
1399
- self.compile_generalized_call_body(args, kwargs, func_name_id, call_pos)?;
1400
- }
1401
- }
1402
- Ok(())
1403
- }
1404
-
1405
- /// Compiles function call arguments and emits the call instruction.
1406
- ///
1407
- /// This is used when the callable is already on the stack (e.g., from compiling an expression).
1408
- /// It compiles the arguments, then emits `CallFunction` or `CallFunctionKw` as appropriate.
1409
- fn compile_call_args(&mut self, args: &ArgExprs, call_pos: CodeRange) -> Result<(), CompileError> {
1410
- match args {
1411
- ArgExprs::Empty => {
1412
- self.code.set_location(call_pos, None);
1413
- self.code.emit_u8(Opcode::CallFunction, 0);
1414
- }
1415
- ArgExprs::One(arg) => {
1416
- self.compile_expr(arg)?;
1417
- self.code.set_location(call_pos, None);
1418
- self.code.emit_u8(Opcode::CallFunction, 1);
1419
- }
1420
- ArgExprs::Two(arg1, arg2) => {
1421
- self.compile_expr(arg1)?;
1422
- self.compile_expr(arg2)?;
1423
- self.code.set_location(call_pos, None);
1424
- self.code.emit_u8(Opcode::CallFunction, 2);
1425
- }
1426
- ArgExprs::Args(args) => {
1427
- if args.len() > MAX_CALL_ARGS {
1428
- return Err(CompileError::new(
1429
- format!("more than {MAX_CALL_ARGS} positional arguments in function call"),
1430
- call_pos,
1431
- ));
1432
- }
1433
- for arg in args {
1434
- self.compile_expr(arg)?;
1435
- }
1436
- let arg_count = u8::try_from(args.len()).expect("argument count exceeds u8");
1437
- self.code.set_location(call_pos, None);
1438
- self.code.emit_u8(Opcode::CallFunction, arg_count);
1439
- }
1440
- ArgExprs::Kwargs(kwargs) => {
1441
- if kwargs.len() > MAX_CALL_ARGS {
1442
- return Err(CompileError::new(
1443
- format!("more than {MAX_CALL_ARGS} keyword arguments in function call"),
1444
- call_pos,
1445
- ));
1446
- }
1447
- let mut kwname_ids = Vec::with_capacity(kwargs.len());
1448
- for kwarg in kwargs {
1449
- self.compile_expr(&kwarg.value)?;
1450
- kwname_ids.push(u16::try_from(kwarg.key.name_id.index()).expect("name index exceeds u16"));
1451
- }
1452
- self.code.set_location(call_pos, None);
1453
- self.code.emit_call_function_kw(0, &kwname_ids);
1454
- }
1455
- ArgExprs::ArgsKargs {
1456
- args,
1457
- kwargs,
1458
- var_args,
1459
- var_kwargs,
1460
- } => {
1461
- // Mixed positional and keyword arguments - may include *args or **kwargs unpacking
1462
- if var_args.is_some() || var_kwargs.is_some() {
1463
- // Use CallFunctionExtended for unpacking - no limit on this path since
1464
- // args are built into a tuple dynamically at runtime.
1465
- // Callable is already on stack, so we just need to build args and kwargs.
1466
- self.compile_call_args_with_unpacking(
1467
- args.as_ref(),
1468
- var_args.as_ref(),
1469
- kwargs.as_ref(),
1470
- var_kwargs.as_ref(),
1471
- call_pos,
1472
- )?;
1473
- } else {
1474
- // No unpacking - use CallFunctionKw for efficiency
1475
- let pos_args = args.as_deref().unwrap_or(&[]);
1476
- let kw_args = kwargs.as_deref().unwrap_or(&[]);
1477
- let pos_count = pos_args.len();
1478
- let kw_count = kw_args.len();
1479
-
1480
- // Check limits separately (same as direct calls)
1481
- if pos_count > MAX_CALL_ARGS {
1482
- return Err(CompileError::new(
1483
- format!("more than {MAX_CALL_ARGS} positional arguments in function call"),
1484
- call_pos,
1485
- ));
1486
- }
1487
- if kw_count > MAX_CALL_ARGS {
1488
- return Err(CompileError::new(
1489
- format!("more than {MAX_CALL_ARGS} keyword arguments in function call"),
1490
- call_pos,
1491
- ));
1492
- }
1493
-
1494
- // Compile positional args
1495
- for arg in pos_args {
1496
- self.compile_expr(arg)?;
1497
- }
1498
-
1499
- // Compile keyword args
1500
- let mut kwname_ids = Vec::with_capacity(kw_count);
1501
- for kwarg in kw_args {
1502
- self.compile_expr(&kwarg.value)?;
1503
- kwname_ids.push(u16::try_from(kwarg.key.name_id.index()).expect("name index exceeds u16"));
1504
- }
1505
-
1506
- self.code.set_location(call_pos, None);
1507
- self.code.emit_call_function_kw(
1508
- u8::try_from(pos_count).expect("positional arg count exceeds u8"),
1509
- &kwname_ids,
1510
- );
1511
- }
1512
- }
1513
- ArgExprs::GeneralizedCall { args, kwargs } => {
1514
- // PEP 448: generalized unpacking — callable is already on the stack.
1515
- // Use 0xFFFF as func_name_id since we don't know the callee name here.
1516
- self.compile_generalized_call_body(args, kwargs, 0xFFFF, call_pos)?;
1517
- }
1518
- }
1519
- Ok(())
1520
- }
1521
-
1522
- /// Compiles arguments with `*args` and/or `**kwargs` unpacking when callable is already on stack.
1523
- ///
1524
- /// This is used for expression calls (e.g., `(lambda *a: a)(*xs)`) where the callable
1525
- /// is compiled as an expression and is already on the stack.
1526
- ///
1527
- /// Stack layout: callable (on stack) -> callable, args_tuple, kwargs_dict?
1528
- fn compile_call_args_with_unpacking(
1529
- &mut self,
1530
- args: Option<&Vec<ExprLoc>>,
1531
- var_args: Option<&ExprLoc>,
1532
- kwargs: Option<&Vec<Kwarg>>,
1533
- var_kwargs: Option<&ExprLoc>,
1534
- call_pos: CodeRange,
1535
- ) -> Result<(), CompileError> {
1536
- // 1. Build args tuple
1537
- // Push regular positional args and build list
1538
- let pos_count = args.map_or(0, Vec::len);
1539
- if let Some(args) = args {
1540
- for arg in args {
1541
- self.compile_expr(arg)?;
1542
- }
1543
- }
1544
- self.code.emit_u16(
1545
- Opcode::BuildList,
1546
- u16::try_from(pos_count).expect("positional arg count exceeds u16"),
1547
- );
1548
-
1549
- // Extend with *args if present
1550
- if let Some(var_args_expr) = var_args {
1551
- self.compile_expr(var_args_expr)?;
1552
- self.code.emit(Opcode::ListExtend);
1553
- }
1554
-
1555
- // Convert list to tuple
1556
- self.code.emit(Opcode::ListToTuple);
1557
-
1558
- // 2. Build kwargs dict (if we have kwargs or var_kwargs)
1559
- let has_kwargs = kwargs.is_some() || var_kwargs.is_some();
1560
- if has_kwargs {
1561
- // Build dict from regular kwargs
1562
- let kw_count = kwargs.map_or(0, Vec::len);
1563
- if let Some(kwargs) = kwargs {
1564
- for kwarg in kwargs {
1565
- // Push key as interned string constant
1566
- let key_const = self.code.add_const(Value::InternString(kwarg.key.name_id));
1567
- self.code.emit_u16(Opcode::LoadConst, key_const);
1568
- // Push value
1569
- self.compile_expr(&kwarg.value)?;
1570
- }
1571
- }
1572
- self.code.emit_u16(
1573
- Opcode::BuildDict,
1574
- u16::try_from(kw_count).expect("keyword count exceeds u16"),
1575
- );
1576
-
1577
- // Merge **kwargs if present
1578
- // Use 0xFFFF for func_name_id (like builtins) since we don't have a name
1579
- if let Some(var_kwargs_expr) = var_kwargs {
1580
- self.compile_expr(var_kwargs_expr)?;
1581
- self.code.emit_u16(Opcode::DictMerge, 0xFFFF);
1582
- }
1583
- }
1584
-
1585
- // 3. Call the function
1586
- self.code.set_location(call_pos, None);
1587
- let flags = u8::from(has_kwargs);
1588
- self.code.emit_u8(Opcode::CallFunctionExtended, flags);
1589
- Ok(())
1590
- }
1591
-
1592
- /// Compiles arguments for a builtin call and returns the arg count if optimization can be used.
1593
- ///
1594
- /// Returns `Some(arg_count)` if the call uses positional-only arguments (CallBuiltinFunction applicable).
1595
- /// Returns `None` if the call uses kwargs or unpacking (must use standard CallFunction path).
1596
- ///
1597
- /// When `Some` is returned, arguments have been compiled onto the stack.
1598
- fn compile_builtin_call(&mut self, args: &ArgExprs, call_pos: CodeRange) -> Result<Option<u8>, CompileError> {
1599
- match args {
1600
- ArgExprs::Empty => Ok(Some(0)),
1601
- ArgExprs::One(arg) => {
1602
- self.compile_expr(arg)?;
1603
- Ok(Some(1))
1604
- }
1605
- ArgExprs::Two(arg1, arg2) => {
1606
- self.compile_expr(arg1)?;
1607
- self.compile_expr(arg2)?;
1608
- Ok(Some(2))
1609
- }
1610
- ArgExprs::Args(args) => {
1611
- if args.len() > MAX_CALL_ARGS {
1612
- return Err(CompileError::new(
1613
- format!("more than {MAX_CALL_ARGS} positional arguments in function call"),
1614
- call_pos,
1615
- ));
1616
- }
1617
- for arg in args {
1618
- self.compile_expr(arg)?;
1619
- }
1620
- Ok(Some(u8::try_from(args.len()).expect("argument count exceeds u8")))
1621
- }
1622
- // Kwargs or unpacking - fall back to standard path
1623
- ArgExprs::Kwargs(_) | ArgExprs::ArgsKargs { .. } | ArgExprs::GeneralizedCall { .. } => Ok(None),
1624
- }
1625
- }
1626
-
1627
- /// Compiles a function call with `*args` and/or `**kwargs` unpacking.
1628
- ///
1629
- /// This generates bytecode to build an args tuple and kwargs dict dynamically,
1630
- /// then calls the function using `CallFunctionEx`.
1631
- ///
1632
- /// Stack layout for call:
1633
- /// - callable (already on stack)
1634
- /// - args tuple
1635
- /// - kwargs dict (if present)
1636
- fn compile_call_with_unpacking(
1637
- &mut self,
1638
- callable: &Callable,
1639
- args: Option<&Vec<ExprLoc>>,
1640
- var_args: Option<&ExprLoc>,
1641
- kwargs: Option<&Vec<Kwarg>>,
1642
- var_kwargs: Option<&ExprLoc>,
1643
- call_pos: CodeRange,
1644
- ) -> Result<(), CompileError> {
1645
- // Get function name for error messages (0xFFFF for builtins)
1646
- let func_name_id = match callable {
1647
- Callable::Name(ident) => u16::try_from(ident.name_id.index()).expect("name index exceeds u16"),
1648
- Callable::Builtin(_) => 0xFFFF,
1649
- };
1650
-
1651
- // 1. Build args tuple
1652
- // Push regular positional args and build list
1653
- let pos_count = args.map_or(0, Vec::len);
1654
- if let Some(args) = args {
1655
- for arg in args {
1656
- self.compile_expr(arg)?;
1657
- }
1658
- }
1659
- self.code.emit_u16(
1660
- Opcode::BuildList,
1661
- u16::try_from(pos_count).expect("positional arg count exceeds u16"),
1662
- );
1663
-
1664
- // Extend with *args if present
1665
- if let Some(var_args_expr) = var_args {
1666
- self.compile_expr(var_args_expr)?;
1667
- self.code.emit(Opcode::ListExtend);
1668
- }
1669
-
1670
- // Convert list to tuple
1671
- self.code.emit(Opcode::ListToTuple);
1672
-
1673
- // 2. Build kwargs dict (if we have kwargs or var_kwargs)
1674
- let has_kwargs = kwargs.is_some() || var_kwargs.is_some();
1675
- if has_kwargs {
1676
- // Build dict from regular kwargs
1677
- let kw_count = kwargs.map_or(0, Vec::len);
1678
- if let Some(kwargs) = kwargs {
1679
- for kwarg in kwargs {
1680
- // Push key as interned string constant
1681
- let key_const = self.code.add_const(Value::InternString(kwarg.key.name_id));
1682
- self.code.emit_u16(Opcode::LoadConst, key_const);
1683
- // Push value
1684
- self.compile_expr(&kwarg.value)?;
1685
- }
1686
- }
1687
- self.code.emit_u16(
1688
- Opcode::BuildDict,
1689
- u16::try_from(kw_count).expect("keyword count exceeds u16"),
1690
- );
1691
-
1692
- // Merge **kwargs if present
1693
- if let Some(var_kwargs_expr) = var_kwargs {
1694
- self.compile_expr(var_kwargs_expr)?;
1695
- self.code.emit_u16(Opcode::DictMerge, func_name_id);
1696
- }
1697
- }
1698
-
1699
- // 3. Call the function
1700
- self.code.set_location(call_pos, None);
1701
- let flags = u8::from(has_kwargs);
1702
- self.code.emit_u8(Opcode::CallFunctionExtended, flags);
1703
- Ok(())
1704
- }
1705
-
1706
- /// Compiles an attribute call on an object.
1707
- ///
1708
- /// The object should already be on the stack. This compiles the arguments
1709
- /// and emits a CallAttr opcode with the attribute name and arg count.
1710
- fn compile_method_call(
1711
- &mut self,
1712
- attr: &EitherStr,
1713
- args: &ArgExprs,
1714
- call_pos: CodeRange,
1715
- ) -> Result<(), CompileError> {
1716
- // Get the interned attribute name
1717
- let name_id = attr.string_id().expect("CallAttr requires interned attr name");
1718
-
1719
- // Compile arguments based on the argument type
1720
- match args {
1721
- ArgExprs::Empty => {
1722
- self.code.set_location(call_pos, None);
1723
- self.code.emit_u16_u8(
1724
- Opcode::CallAttr,
1725
- u16::try_from(name_id.index()).expect("name index exceeds u16"),
1726
- 0,
1727
- );
1728
- }
1729
- ArgExprs::One(arg) => {
1730
- self.compile_expr(arg)?;
1731
- self.code.set_location(call_pos, None);
1732
- self.code.emit_u16_u8(
1733
- Opcode::CallAttr,
1734
- u16::try_from(name_id.index()).expect("name index exceeds u16"),
1735
- 1,
1736
- );
1737
- }
1738
- ArgExprs::Two(arg1, arg2) => {
1739
- self.compile_expr(arg1)?;
1740
- self.compile_expr(arg2)?;
1741
- self.code.set_location(call_pos, None);
1742
- self.code.emit_u16_u8(
1743
- Opcode::CallAttr,
1744
- u16::try_from(name_id.index()).expect("name index exceeds u16"),
1745
- 2,
1746
- );
1747
- }
1748
- ArgExprs::Args(args) => {
1749
- // Check argument count limit
1750
- if args.len() > MAX_CALL_ARGS {
1751
- return Err(CompileError::new(
1752
- format!("more than {MAX_CALL_ARGS} arguments in method call"),
1753
- call_pos,
1754
- ));
1755
- }
1756
- for arg in args {
1757
- self.compile_expr(arg)?;
1758
- }
1759
- let arg_count = u8::try_from(args.len()).expect("argument count exceeds u8");
1760
- self.code.set_location(call_pos, None);
1761
- self.code.emit_u16_u8(
1762
- Opcode::CallAttr,
1763
- u16::try_from(name_id.index()).expect("name index exceeds u16"),
1764
- arg_count,
1765
- );
1766
- }
1767
- ArgExprs::Kwargs(kwargs) => {
1768
- // Keyword-only method call
1769
- if kwargs.len() > MAX_CALL_ARGS {
1770
- return Err(CompileError::new(
1771
- format!("more than {MAX_CALL_ARGS} keyword arguments in method call"),
1772
- call_pos,
1773
- ));
1774
- }
1775
- // Compile kwarg values and collect names
1776
- let mut kwname_ids = Vec::with_capacity(kwargs.len());
1777
- for kwarg in kwargs {
1778
- self.compile_expr(&kwarg.value)?;
1779
- kwname_ids.push(u16::try_from(kwarg.key.name_id.index()).expect("name index exceeds u16"));
1780
- }
1781
- self.code.set_location(call_pos, None);
1782
- self.code.emit_call_attr_kw(
1783
- u16::try_from(name_id.index()).expect("name index exceeds u16"),
1784
- 0, // no positional args
1785
- &kwname_ids,
1786
- );
1787
- }
1788
- ArgExprs::ArgsKargs {
1789
- args,
1790
- kwargs,
1791
- var_args,
1792
- var_kwargs,
1793
- } => {
1794
- // Check if there's unpacking - use CallAttrExtended
1795
- if var_args.is_some() || var_kwargs.is_some() {
1796
- return self.compile_method_call_with_unpacking(
1797
- name_id,
1798
- args.as_ref(),
1799
- var_args.as_ref(),
1800
- kwargs.as_ref(),
1801
- var_kwargs.as_ref(),
1802
- call_pos,
1803
- );
1804
- }
1805
-
1806
- // No unpacking - use CallAttrKw for efficiency
1807
- let pos_count = args.as_ref().map_or(0, Vec::len);
1808
- let kw_count = kwargs.as_ref().map_or(0, Vec::len);
1809
-
1810
- if pos_count > MAX_CALL_ARGS {
1811
- return Err(CompileError::new(
1812
- format!("more than {MAX_CALL_ARGS} positional arguments in method call"),
1813
- call_pos,
1814
- ));
1815
- }
1816
- if kw_count > MAX_CALL_ARGS {
1817
- return Err(CompileError::new(
1818
- format!("more than {MAX_CALL_ARGS} keyword arguments in method call"),
1819
- call_pos,
1820
- ));
1821
- }
1822
-
1823
- // Compile positional args
1824
- if let Some(args) = args {
1825
- for arg in args {
1826
- self.compile_expr(arg)?;
1827
- }
1828
- }
1829
-
1830
- // Compile kwarg values and collect names
1831
- let mut kwname_ids = Vec::new();
1832
- if let Some(kwargs) = kwargs {
1833
- for kwarg in kwargs {
1834
- self.compile_expr(&kwarg.value)?;
1835
- kwname_ids.push(u16::try_from(kwarg.key.name_id.index()).expect("name index exceeds u16"));
1836
- }
1837
- }
1838
-
1839
- self.code.set_location(call_pos, None);
1840
- self.code.emit_call_attr_kw(
1841
- u16::try_from(name_id.index()).expect("name index exceeds u16"),
1842
- u8::try_from(pos_count).expect("positional arg count exceeds u8"),
1843
- &kwname_ids,
1844
- );
1845
- }
1846
- ArgExprs::GeneralizedCall { args, kwargs } => {
1847
- // PEP 448: generalized unpacking on a method call.
1848
- // Receiver is already on the stack; build args tuple and kwargs dict,
1849
- // then emit CallAttrExtended.
1850
- let func_name_id = u16::try_from(name_id.index()).expect("name index exceeds u16");
1851
- let has_kwargs = !kwargs.is_empty();
1852
-
1853
- // 1. Build args tuple
1854
- self.code.emit_u16(Opcode::BuildList, 0);
1855
- for arg in args {
1856
- match arg {
1857
- CallArg::Value(e) => {
1858
- self.compile_expr(e)?;
1859
- self.code.emit_u8(Opcode::ListAppend, 0);
1860
- }
1861
- CallArg::Unpack(e) => {
1862
- self.compile_expr(e)?;
1863
- self.code.emit(Opcode::ListExtend);
1864
- }
1865
- }
1866
- }
1867
- self.code.emit(Opcode::ListToTuple);
1868
-
1869
- // 2. Build kwargs dict (if any)
1870
- if has_kwargs {
1871
- self.code.emit_u16(Opcode::BuildDict, 0);
1872
- for kwarg in kwargs {
1873
- match kwarg {
1874
- CallKwarg::Named(kw) => {
1875
- let key_const = self.code.add_const(Value::InternString(kw.key.name_id));
1876
- self.code.emit_u16(Opcode::LoadConst, key_const);
1877
- self.compile_expr(&kw.value)?;
1878
- self.code.emit_u16(Opcode::BuildDict, 1);
1879
- self.code.emit_u16(Opcode::DictMerge, func_name_id);
1880
- }
1881
- CallKwarg::Unpack(e) => {
1882
- self.compile_expr(e)?;
1883
- self.code.emit_u16(Opcode::DictMerge, func_name_id);
1884
- }
1885
- }
1886
- }
1887
- }
1888
-
1889
- // 3. Emit CallAttrExtended
1890
- self.code.set_location(call_pos, None);
1891
- let flags = u8::from(has_kwargs);
1892
- self.code.emit_u16_u8(Opcode::CallAttrExtended, func_name_id, flags);
1893
- }
1894
- }
1895
- Ok(())
1896
- }
1897
-
1898
- /// Compiles a method call with `*args` and/or `**kwargs` unpacking.
1899
- ///
1900
- /// The receiver object should already be on the stack. This builds the args tuple
1901
- /// and optional kwargs dict, then emits `CallAttrExtended`.
1902
- fn compile_method_call_with_unpacking(
1903
- &mut self,
1904
- name_id: StringId,
1905
- args: Option<&Vec<ExprLoc>>,
1906
- var_args: Option<&ExprLoc>,
1907
- kwargs: Option<&Vec<Kwarg>>,
1908
- var_kwargs: Option<&ExprLoc>,
1909
- call_pos: CodeRange,
1910
- ) -> Result<(), CompileError> {
1911
- // 1. Build args tuple
1912
- // Push regular positional args and build list
1913
- let pos_count = args.map_or(0, Vec::len);
1914
- if let Some(args) = args {
1915
- for arg in args {
1916
- self.compile_expr(arg)?;
1917
- }
1918
- }
1919
- self.code.emit_u16(
1920
- Opcode::BuildList,
1921
- u16::try_from(pos_count).expect("positional arg count exceeds u16"),
1922
- );
1923
-
1924
- // Extend with *args if present
1925
- if let Some(var_args_expr) = var_args {
1926
- self.compile_expr(var_args_expr)?;
1927
- self.code.emit(Opcode::ListExtend);
1928
- }
1929
-
1930
- // Convert list to tuple
1931
- self.code.emit(Opcode::ListToTuple);
1932
-
1933
- // 2. Build kwargs dict (if we have kwargs or var_kwargs)
1934
- let has_kwargs = kwargs.is_some() || var_kwargs.is_some();
1935
- if has_kwargs {
1936
- // Build dict from regular kwargs
1937
- let kw_count = kwargs.map_or(0, Vec::len);
1938
- if let Some(kwargs) = kwargs {
1939
- for kwarg in kwargs {
1940
- // Push key as interned string constant
1941
- let key_const = self.code.add_const(Value::InternString(kwarg.key.name_id));
1942
- self.code.emit_u16(Opcode::LoadConst, key_const);
1943
- // Push value
1944
- self.compile_expr(&kwarg.value)?;
1945
- }
1946
- }
1947
- self.code.emit_u16(
1948
- Opcode::BuildDict,
1949
- u16::try_from(kw_count).expect("keyword count exceeds u16"),
1950
- );
1951
-
1952
- // Merge **kwargs if present
1953
- if let Some(var_kwargs_expr) = var_kwargs {
1954
- self.compile_expr(var_kwargs_expr)?;
1955
- // Use the method name for error messages
1956
- self.code.emit_u16(
1957
- Opcode::DictMerge,
1958
- u16::try_from(name_id.index()).expect("name index exceeds u16"),
1959
- );
1960
- }
1961
- }
1962
-
1963
- // 3. Call the method with CallAttrExtended
1964
- self.code.set_location(call_pos, None);
1965
- let name_idx = u16::try_from(name_id.index()).expect("name index exceeds u16");
1966
- let flags = u8::from(has_kwargs);
1967
- self.code.emit_u16_u8(Opcode::CallAttrExtended, name_idx, flags);
1968
- Ok(())
1969
- }
1970
-
1971
- /// Shared body for PEP 448 generalized calls with multiple `*args` and/or `**kwargs`.
1972
- ///
1973
- /// Assumes the callable is already on the stack (pushed by the caller).
1974
- /// Emits:
1975
- /// 1. `BuildList(0)` + per-item `ListAppend`/`ListExtend` + `ListToTuple` for args.
1976
- /// 2. `BuildDict(0)` + per-item `BuildDict(1)+DictMerge`/`DictMerge` for kwargs (if any).
1977
- /// 3. `CallFunctionExtended(flags)`.
1978
- ///
1979
- /// `func_name_id` is used in `DictMerge` error messages; pass `0xFFFF` when unknown.
1980
- ///
1981
- /// Stack transition (callable already on stack):
1982
- /// `[callable]` → `[callable, args_tuple]` → `[callable, args_tuple, kwargs_dict?]`
1983
- /// → `[result]`
1984
- fn compile_generalized_call_body(
1985
- &mut self,
1986
- args: &[CallArg],
1987
- kwargs: &[CallKwarg],
1988
- func_name_id: u16,
1989
- call_pos: CodeRange,
1990
- ) -> Result<(), CompileError> {
1991
- // 1. Build args tuple
1992
- self.code.emit_u16(Opcode::BuildList, 0);
1993
- for arg in args {
1994
- match arg {
1995
- CallArg::Value(e) => {
1996
- self.compile_expr(e)?;
1997
- self.code.emit_u8(Opcode::ListAppend, 0);
1998
- }
1999
- CallArg::Unpack(e) => {
2000
- self.compile_expr(e)?;
2001
- self.code.emit(Opcode::ListExtend);
2002
- }
2003
- }
2004
- }
2005
- self.code.emit(Opcode::ListToTuple);
2006
-
2007
- // 2. Build kwargs dict (if any)
2008
- let has_kwargs = !kwargs.is_empty();
2009
- if has_kwargs {
2010
- // Start with an empty dict, then merge each kwarg one at a time via DictMerge
2011
- // so that duplicates (including Named+Unpack ordering) raise TypeError correctly.
2012
- self.code.emit_u16(Opcode::BuildDict, 0);
2013
- for kwarg in kwargs {
2014
- match kwarg {
2015
- CallKwarg::Named(kw) => {
2016
- // Wrap key+value in a single-item dict, then merge into kwargs dict.
2017
- let key_const = self.code.add_const(Value::InternString(kw.key.name_id));
2018
- self.code.emit_u16(Opcode::LoadConst, key_const);
2019
- self.compile_expr(&kw.value)?;
2020
- self.code.emit_u16(Opcode::BuildDict, 1);
2021
- self.code.emit_u16(Opcode::DictMerge, func_name_id);
2022
- }
2023
- CallKwarg::Unpack(e) => {
2024
- self.compile_expr(e)?;
2025
- self.code.emit_u16(Opcode::DictMerge, func_name_id);
2026
- }
2027
- }
2028
- }
2029
- }
2030
-
2031
- // 3. Emit the extended call
2032
- self.code.set_location(call_pos, None);
2033
- let flags = u8::from(has_kwargs);
2034
- self.code.emit_u8(Opcode::CallFunctionExtended, flags);
2035
- Ok(())
2036
- }
2037
-
2038
- /// Compiles a for loop.
2039
- fn compile_for(
2040
- &mut self,
2041
- target: &UnpackTarget,
2042
- iter: &ExprLoc,
2043
- body: &[PreparedNode],
2044
- or_else: &[PreparedNode],
2045
- ) -> Result<(), CompileError> {
2046
- // Record stack depth at loop start (before iterator is pushed)
2047
- // This is the depth we return to when the loop finishes (iterator popped)
2048
- let loop_exit_depth = self.code.stack_depth();
2049
-
2050
- // Compile iterator expression
2051
- self.compile_expr(iter)?;
2052
- // Convert to iterator
2053
- self.code.emit(Opcode::GetIter);
2054
-
2055
- // Loop start
2056
- let loop_start = self.code.current_offset();
2057
-
2058
- // Push loop info for break/continue
2059
- self.loop_stack.push(LoopInfo {
2060
- start: loop_start,
2061
- break_jumps: Vec::new(),
2062
- has_iterator_on_stack: true,
2063
- });
2064
-
2065
- // ForIter: advance iterator or jump to end
2066
- let end_jump = self.code.emit_jump(Opcode::ForIter);
2067
-
2068
- // Store current value to target (handles both single identifiers and tuple unpacking)
2069
- self.compile_unpack_target(target);
2070
-
2071
- // Compile body
2072
- self.compile_block(body)?;
2073
-
2074
- // Jump back to loop start
2075
- self.code.emit_jump_to(Opcode::Jump, loop_start);
2076
-
2077
- // End of loop - ForIter jumps here when iterator is exhausted
2078
- self.code.patch_jump(end_jump);
2079
- // Iterator is popped when loop ends normally, so restore depth to before loop
2080
- self.code.set_stack_depth(loop_exit_depth);
2081
-
2082
- // Pop loop info before compiling else block
2083
- let loop_info = self.loop_stack.pop().expect("loop stack underflow");
2084
-
2085
- // Compile else block (runs if loop completed without break)
2086
- if !or_else.is_empty() {
2087
- self.compile_block(or_else)?;
2088
- }
2089
-
2090
- // Patch break jumps to here - AFTER the else block so break skips else
2091
- for break_jump in loop_info.break_jumps {
2092
- self.code.patch_jump(break_jump);
2093
- }
2094
-
2095
- Ok(())
2096
- }
2097
-
2098
- /// Compiles a while loop.
2099
- ///
2100
- /// The bytecode structure:
2101
- /// ```text
2102
- /// loop_start:
2103
- /// [evaluate condition]
2104
- /// JumpIfFalse -> end_jump
2105
- /// [body]
2106
- /// Jump -> loop_start
2107
- /// end_jump:
2108
- /// [else block]
2109
- /// [break patches here]
2110
- /// ```
2111
- ///
2112
- /// Key differences from `for` loops:
2113
- /// - No `GetIter` (no iterator)
2114
- /// - No `ForIter` (use `JumpIfFalse` instead)
2115
- /// - `continue` jumps to condition evaluation
2116
- /// - `break` doesn't need to pop iterator (nothing extra on stack)
2117
- fn compile_while(
2118
- &mut self,
2119
- test: &ExprLoc,
2120
- body: &[PreparedNode],
2121
- or_else: &[PreparedNode],
2122
- ) -> Result<(), CompileError> {
2123
- let loop_start = self.code.current_offset();
2124
-
2125
- self.loop_stack.push(LoopInfo {
2126
- start: loop_start,
2127
- break_jumps: Vec::new(),
2128
- has_iterator_on_stack: false,
2129
- });
2130
-
2131
- self.compile_expr(test)?;
2132
- let end_jump = self.code.emit_jump(Opcode::JumpIfFalse);
2133
-
2134
- self.compile_block(body)?;
2135
- self.code.emit_jump_to(Opcode::Jump, loop_start);
2136
-
2137
- self.code.patch_jump(end_jump);
2138
- let loop_info = self.loop_stack.pop().expect("loop stack underflow");
2139
-
2140
- if !or_else.is_empty() {
2141
- self.compile_block(or_else)?;
2142
- }
2143
-
2144
- for break_jump in loop_info.break_jumps {
2145
- self.code.patch_jump(break_jump);
2146
- }
2147
-
2148
- Ok(())
2149
- }
2150
-
2151
- /// Compiles a break statement.
2152
- ///
2153
- /// Break exits the innermost loop and skips its else block. If inside a
2154
- /// try-finally, the finally block must run first.
2155
- ///
2156
- /// The bytecode without finally:
2157
- /// 1. Clean up exception state if inside except handler
2158
- /// 2. Pop the iterator if in a `for` loop (still on stack during loop body)
2159
- /// 3. Jump to after the else block
2160
- ///
2161
- /// With finally:
2162
- /// 1. Clean up exception state if inside except handler
2163
- /// 2. Pop the iterator if in a `for` loop
2164
- /// 3. Jump to "finally with break" path (patched when try compilation completes)
2165
- /// 4. That path runs finally, then jumps to after the else block
2166
- fn compile_break(&mut self, position: CodeRange) -> Result<(), CompileError> {
2167
- if self.loop_stack.is_empty() {
2168
- return Err(CompileError::new("'break' outside loop", position));
2169
- }
2170
-
2171
- let target_loop_depth = self.loop_stack.len() - 1;
2172
-
2173
- // If inside except handlers, clean up ALL exception states
2174
- // Each nested except handler has pushed an exception onto the stack,
2175
- // so we need to clear/pop each one when breaking out
2176
- for _ in 0..self.except_handler_depth {
2177
- self.code.emit(Opcode::ClearException);
2178
- self.code.emit(Opcode::Pop); // Pop the exception value
2179
- }
2180
-
2181
- // Pop the iterator only for `for` loops (has iterator on stack)
2182
- // `while` loops don't have an iterator to pop
2183
- if self.loop_stack[target_loop_depth].has_iterator_on_stack {
2184
- self.code.emit(Opcode::Pop);
2185
- }
2186
-
2187
- // Check if we need to go through any finally blocks
2188
- // We need to run finally if break crosses the try boundary, i.e., if
2189
- // we're breaking from a loop that existed before the try started.
2190
- if let Some(finally_target) = self.finally_targets.last_mut()
2191
- && target_loop_depth < finally_target.loop_depth_at_entry
2192
- {
2193
- // Breaking from a loop that's outside (or at the start of) this try-finally,
2194
- // so finally must run before the break
2195
- let jump = self.code.emit_jump(Opcode::Jump);
2196
- finally_target.break_jumps.push(BreakContinueThruFinally {
2197
- jump,
2198
- target_loop_depth,
2199
- });
2200
- // Set stack depth for unreachable cleanup code (see comment below)
2201
- if self.except_handler_depth > 0 {
2202
- self.code
2203
- .set_stack_depth(u16::try_from(self.except_handler_depth).unwrap_or(u16::MAX));
2204
- }
2205
- return Ok(());
2206
- }
2207
-
2208
- // No finally to go through, jump directly to loop end
2209
- let jump = self.code.emit_jump(Opcode::Jump);
2210
- self.loop_stack[target_loop_depth].break_jumps.push(jump);
2211
-
2212
- // The code following this break is unreachable at runtime, but the compiler
2213
- // will still emit cleanup code for each enclosing except handler (ClearException + Pop).
2214
- // Set stack depth so those unreachable pops don't cause negative stack tracking.
2215
- if self.except_handler_depth > 0 {
2216
- self.code
2217
- .set_stack_depth(u16::try_from(self.except_handler_depth).unwrap_or(u16::MAX));
2218
- }
2219
-
2220
- Ok(())
2221
- }
2222
-
2223
- /// Compiles a continue statement.
2224
- ///
2225
- /// Continue jumps back to the loop start (the ForIter instruction) which
2226
- /// advances the iterator and either enters the next iteration or exits the loop.
2227
- /// If inside a try-finally, the finally block must run first.
2228
- fn compile_continue(&mut self, position: CodeRange) -> Result<(), CompileError> {
2229
- if self.loop_stack.is_empty() {
2230
- return Err(CompileError::new("'continue' not properly in loop", position));
2231
- }
2232
-
2233
- let target_loop_depth = self.loop_stack.len() - 1;
2234
-
2235
- // If inside except handlers, clean up ALL exception states
2236
- // Each nested except handler has pushed an exception onto the stack,
2237
- // so we need to clear/pop each one when continuing
2238
- for _ in 0..self.except_handler_depth {
2239
- self.code.emit(Opcode::ClearException);
2240
- self.code.emit(Opcode::Pop); // Pop the exception value
2241
- }
2242
-
2243
- // Check if we need to go through any finally blocks
2244
- // We need to run finally if continue crosses the try boundary
2245
- if let Some(finally_target) = self.finally_targets.last_mut()
2246
- && target_loop_depth < finally_target.loop_depth_at_entry
2247
- {
2248
- // Continuing a loop that's outside (or at the start of) this try-finally,
2249
- // so finally must run before the continue
2250
- let jump = self.code.emit_jump(Opcode::Jump);
2251
- finally_target.continue_jumps.push(BreakContinueThruFinally {
2252
- jump,
2253
- target_loop_depth,
2254
- });
2255
- // Set stack depth for unreachable cleanup code (see comment below)
2256
- if self.except_handler_depth > 0 {
2257
- self.code
2258
- .set_stack_depth(u16::try_from(self.except_handler_depth).unwrap_or(u16::MAX));
2259
- }
2260
- return Ok(());
2261
- }
2262
-
2263
- // No finally to go through, jump directly to loop start
2264
- let loop_start = self.loop_stack[target_loop_depth].start;
2265
- self.code.emit_jump_to(Opcode::Jump, loop_start);
2266
-
2267
- // The code following this continue is unreachable at runtime, but the compiler
2268
- // will still emit cleanup code for each enclosing except handler (ClearException + Pop).
2269
- // Set stack depth so those unreachable pops don't cause negative stack tracking.
2270
- if self.except_handler_depth > 0 {
2271
- self.code
2272
- .set_stack_depth(u16::try_from(self.except_handler_depth).unwrap_or(u16::MAX));
2273
- }
2274
-
2275
- Ok(())
2276
- }
2277
-
2278
- /// Compiles break or continue after a finally block has run.
2279
- ///
2280
- /// Called from `compile_try` after the finally block code. Each control flow
2281
- /// statement may target a different loop, so we check if there's another finally
2282
- /// to go through or if we can jump directly to the loop's target.
2283
- ///
2284
- /// Note: All items in the list jumped to the same finally block, so they all
2285
- /// have the same starting point. After finally runs, we need to route each
2286
- /// to its target loop, potentially through more finally blocks.
2287
- fn compile_control_flow_after_finally(&mut self, items: &[BreakContinueThruFinally], is_break: bool) {
2288
- // All items went through the same finally, now we need to dispatch to
2289
- // potentially different loops. For simplicity, we assume all items in
2290
- // a single finally target the same loop (the innermost one at the time).
2291
- // This is always true since break/continue only targets the innermost loop.
2292
- let Some(first) = items.first() else {
2293
- return;
2294
- };
2295
- let target_loop_depth = first.target_loop_depth;
2296
-
2297
- // Check if there's another finally between us and the target loop
2298
- if let Some(finally_target) = self.finally_targets.last_mut()
2299
- && target_loop_depth < finally_target.loop_depth_at_entry
2300
- {
2301
- // Need to go through another finally
2302
- let jump = self.code.emit_jump(Opcode::Jump);
2303
- let jump_info = BreakContinueThruFinally {
2304
- jump,
2305
- target_loop_depth,
2306
- };
2307
- if is_break {
2308
- finally_target.break_jumps.push(jump_info);
2309
- } else {
2310
- // else continue
2311
- finally_target.continue_jumps.push(jump_info);
2312
- }
2313
- return;
2314
- }
2315
-
2316
- // No more finally blocks, jump directly to the loop target
2317
- if is_break {
2318
- let jump = self.code.emit_jump(Opcode::Jump);
2319
- self.loop_stack[target_loop_depth].break_jumps.push(jump);
2320
- } else {
2321
- // else continue
2322
- let loop_start = self.loop_stack[target_loop_depth].start;
2323
- self.code.emit_jump_to(Opcode::Jump, loop_start);
2324
- }
2325
- }
2326
-
2327
- // ========================================================================
2328
- // Comprehension Compilation
2329
- // ========================================================================
2330
-
2331
- /// Compiles a list comprehension: `[elt for target in iter if cond...]`
2332
- ///
2333
- /// Bytecode structure:
2334
- /// ```text
2335
- /// BUILD_LIST 0 ; empty result
2336
- /// <compile first iter>
2337
- /// GET_ITER
2338
- /// loop_start:
2339
- /// FOR_ITER end_loop
2340
- /// STORE_LOCAL target
2341
- /// <compile filters - jump back to loop_start if any fails>
2342
- /// [nested generators...]
2343
- /// <compile elt>
2344
- /// LIST_APPEND depth
2345
- /// JUMP loop_start
2346
- /// end_loop:
2347
- /// ; result list on stack
2348
- /// ```
2349
- fn compile_list_comp(&mut self, elt: &ExprLoc, generators: &[Comprehension]) -> Result<(), CompileError> {
2350
- // Build empty list
2351
- self.code.emit_u16(Opcode::BuildList, 0);
2352
-
2353
- // Compile the nested generators, which will eventually append to the list
2354
- let depth = u8::try_from(generators.len()).expect("too many generators in list comprehension");
2355
- self.compile_comprehension_generators(generators, 0, |compiler| {
2356
- compiler.compile_expr(elt)?;
2357
- compiler.code.emit_u8(Opcode::ListAppend, depth);
2358
- Ok(())
2359
- })?;
2360
-
2361
- Ok(())
2362
- }
2363
-
2364
- /// Compiles a set comprehension: `{elt for target in iter if cond...}`
2365
- fn compile_set_comp(&mut self, elt: &ExprLoc, generators: &[Comprehension]) -> Result<(), CompileError> {
2366
- // Build empty set
2367
- self.code.emit_u16(Opcode::BuildSet, 0);
2368
-
2369
- // Compile the nested generators, which will eventually add to the set
2370
- let depth = u8::try_from(generators.len()).expect("too many generators in set comprehension");
2371
- self.compile_comprehension_generators(generators, 0, |compiler| {
2372
- compiler.compile_expr(elt)?;
2373
- compiler.code.emit_u8(Opcode::SetAdd, depth);
2374
- Ok(())
2375
- })?;
2376
-
2377
- Ok(())
2378
- }
2379
-
2380
- /// Compiles a dict comprehension: `{key: value for target in iter if cond...}`
2381
- fn compile_dict_comp(
2382
- &mut self,
2383
- key: &ExprLoc,
2384
- value: &ExprLoc,
2385
- generators: &[Comprehension],
2386
- ) -> Result<(), CompileError> {
2387
- // Build empty dict
2388
- self.code.emit_u16(Opcode::BuildDict, 0);
2389
-
2390
- // Compile the nested generators, which will eventually set items in the dict
2391
- let depth = u8::try_from(generators.len()).expect("too many generators in dict comprehension");
2392
- self.compile_comprehension_generators(generators, 0, |compiler| {
2393
- compiler.compile_expr(key)?;
2394
- compiler.compile_expr(value)?;
2395
- compiler.code.emit_u8(Opcode::DictSetItem, depth);
2396
- Ok(())
2397
- })?;
2398
-
2399
- Ok(())
2400
- }
2401
-
2402
- /// Recursively compiles comprehension generators (the for/if clauses).
2403
- ///
2404
- /// For each generator:
2405
- /// 1. Compile the iterator expression and get iterator
2406
- /// 2. Start loop: FOR_ITER to get next value or exit
2407
- /// 3. Store to target variable
2408
- /// 4. Compile filter conditions (jump back to loop start if any fails)
2409
- /// 5. Either recurse for inner generator, or call the body callback
2410
- /// 6. Jump back to loop start
2411
- ///
2412
- /// The `body_fn` callback is called at the innermost level to emit the element/key-value code.
2413
- fn compile_comprehension_generators(
2414
- &mut self,
2415
- generators: &[Comprehension],
2416
- index: usize,
2417
- body_fn: impl FnOnce(&mut Self) -> Result<(), CompileError>,
2418
- ) -> Result<(), CompileError> {
2419
- let generator = &generators[index];
2420
-
2421
- // Record stack depth before iterator expression
2422
- // This is the depth we return to when the loop finishes (iterator popped)
2423
- let loop_exit_depth = self.code.stack_depth();
2424
-
2425
- // Compile iterator expression
2426
- self.compile_expr(&generator.iter)?;
2427
- self.code.emit(Opcode::GetIter);
2428
-
2429
- // Loop start
2430
- let loop_start = self.code.current_offset();
2431
-
2432
- // FOR_ITER: advance iterator or jump to end
2433
- let end_jump = self.code.emit_jump(Opcode::ForIter);
2434
-
2435
- // Store current value to target (single variable or tuple unpacking)
2436
- self.compile_unpack_target(&generator.target);
2437
-
2438
- // Compile filter conditions - jump back to loop start if any fails
2439
- for cond in &generator.ifs {
2440
- self.compile_expr(cond)?;
2441
- // If condition is false, skip to next iteration
2442
- self.code.emit_jump_to(Opcode::JumpIfFalse, loop_start);
2443
- }
2444
-
2445
- // Either recurse for inner generator, or emit body
2446
- if index + 1 < generators.len() {
2447
- // Recurse for inner generator
2448
- self.compile_comprehension_generators(generators, index + 1, body_fn)?;
2449
- } else {
2450
- // Innermost level - emit body (the element/key-value expression and append/add/set)
2451
- body_fn(self)?;
2452
- }
2453
-
2454
- // Jump back to loop start
2455
- self.code.emit_jump_to(Opcode::Jump, loop_start);
2456
-
2457
- // End of loop
2458
- self.code.patch_jump(end_jump);
2459
- // Iterator is popped when loop ends normally, so restore depth to before loop
2460
- self.code.set_stack_depth(loop_exit_depth);
2461
-
2462
- Ok(())
2463
- }
2464
-
2465
- /// Compiles storage of an unpack target - either a single identifier, nested tuple, or starred.
2466
- ///
2467
- /// For single identifiers: emits a simple store.
2468
- /// For nested tuples: emits `UnpackSequence` (or `UnpackEx` with starred) and recursively
2469
- /// handles each sub-target.
2470
- fn compile_unpack_target(&mut self, target: &UnpackTarget) {
2471
- match target {
2472
- UnpackTarget::Name(ident) => {
2473
- // Single identifier - just store directly
2474
- self.compile_store(ident);
2475
- }
2476
- UnpackTarget::Starred(ident) => {
2477
- // Starred target by itself (shouldn't happen at top level normally)
2478
- // Just store as if it were a name
2479
- self.compile_store(ident);
2480
- }
2481
- UnpackTarget::Tuple { targets, position } => {
2482
- // Check if there's a starred target
2483
- let star_idx = targets.iter().position(|t| matches!(t, UnpackTarget::Starred(_)));
2484
-
2485
- self.code.set_location(*position, None);
2486
-
2487
- if let Some(star_idx) = star_idx {
2488
- // Has starred target - use UnpackEx
2489
- let before = u8::try_from(star_idx).expect("too many targets before star");
2490
- let after = u8::try_from(targets.len() - star_idx - 1).expect("too many targets after star");
2491
- self.code.emit_u8_u8(Opcode::UnpackEx, before, after);
2492
- } else {
2493
- // No starred target - use UnpackSequence
2494
- let count = u8::try_from(targets.len()).expect("too many targets in nested unpack");
2495
- self.code.emit_u8(Opcode::UnpackSequence, count);
2496
- }
2497
-
2498
- // After UnpackSequence/UnpackEx, values are on stack with first item on top
2499
- // Store them in order, recursively handling further nesting
2500
- for target in targets {
2501
- self.compile_unpack_target(target);
2502
- }
2503
- }
2504
- }
2505
- }
2506
-
2507
- // ========================================================================
2508
- // Statement Helpers
2509
- // ========================================================================
2510
-
2511
- /// Compiles an assert statement.
2512
- fn compile_assert(&mut self, test: &ExprLoc, msg: Option<&ExprLoc>) -> Result<(), CompileError> {
2513
- // Compile test
2514
- self.compile_expr(test)?;
2515
- // Jump over raise if truthy
2516
- let skip_jump = self.code.emit_jump(Opcode::JumpIfTrue);
2517
-
2518
- // Raise AssertionError
2519
- let exc_idx = self
2520
- .code
2521
- .add_const(Value::Builtin(Builtins::ExcType(ExcType::AssertionError)));
2522
- self.code.emit_u16(Opcode::LoadConst, exc_idx);
2523
-
2524
- if let Some(msg_expr) = msg {
2525
- // Call AssertionError(msg)
2526
- self.compile_expr(msg_expr)?;
2527
- self.code.emit_u8(Opcode::CallFunction, 1);
2528
- } else {
2529
- // Call AssertionError()
2530
- self.code.emit_u8(Opcode::CallFunction, 0);
2531
- }
2532
-
2533
- self.code.emit(Opcode::Raise);
2534
- self.code.patch_jump(skip_jump);
2535
- Ok(())
2536
- }
2537
-
2538
- /// Compiles f-string parts, returning the number of string parts to concatenate.
2539
- ///
2540
- /// Each part is compiled to leave a string value on the stack:
2541
- /// - `Literal(StringId)`: Push the interned string directly
2542
- /// - `Interpolation`: Compile expr, emit FormatValue to convert to string
2543
- fn compile_fstring_parts(&mut self, parts: &[FStringPart]) -> Result<u16, CompileError> {
2544
- let mut count = 0u16;
2545
-
2546
- for part in parts {
2547
- match part {
2548
- FStringPart::Literal(string_id) => {
2549
- // Push the interned string as a constant
2550
- let const_idx = self.code.add_const(Value::InternString(*string_id));
2551
- self.code.emit_u16(Opcode::LoadConst, const_idx);
2552
- count += 1;
2553
- }
2554
- FStringPart::Interpolation {
2555
- expr,
2556
- conversion,
2557
- format_spec,
2558
- debug_prefix,
2559
- } => {
2560
- // If debug prefix present, push it first
2561
- if let Some(prefix_id) = debug_prefix {
2562
- let const_idx = self.code.add_const(Value::InternString(*prefix_id));
2563
- self.code.emit_u16(Opcode::LoadConst, const_idx);
2564
- count += 1;
2565
- }
2566
-
2567
- // Compile the expression
2568
- self.compile_expr(expr)?;
2569
-
2570
- // For debug expressions without explicit conversion, Python uses repr by default
2571
- let effective_conversion = if debug_prefix.is_some() && matches!(conversion, ConversionFlag::None) {
2572
- ConversionFlag::Repr
2573
- } else {
2574
- *conversion
2575
- };
2576
-
2577
- // Emit FormatValue with appropriate flags
2578
- let flags = self.compile_format_value(effective_conversion, format_spec.as_ref())?;
2579
- self.code.emit_u8(Opcode::FormatValue, flags);
2580
- count += 1;
2581
- }
2582
- }
2583
- }
2584
-
2585
- Ok(count)
2586
- }
2587
-
2588
- /// Compiles format value flags and optionally pushes format spec to stack.
2589
- ///
2590
- /// Returns the flags byte encoding conversion and format spec presence.
2591
- /// If a format spec is present, it's pushed to the stack before the value.
2592
- fn compile_format_value(
2593
- &mut self,
2594
- conversion: ConversionFlag,
2595
- format_spec: Option<&FormatSpec>,
2596
- ) -> Result<u8, CompileError> {
2597
- // Conversion flag: bits 0-1
2598
- let conv_bits = match conversion {
2599
- ConversionFlag::None => 0,
2600
- ConversionFlag::Str => 1,
2601
- ConversionFlag::Repr => 2,
2602
- ConversionFlag::Ascii => 3,
2603
- };
2604
-
2605
- match format_spec {
2606
- None => Ok(conv_bits),
2607
- Some(FormatSpec::Static(parsed)) => {
2608
- // Static format spec - push a marker constant with the parsed spec info
2609
- // We store this as a special format spec value in the constant pool
2610
- // The VM will recognize this and use the pre-parsed spec
2611
- let const_idx = self.add_format_spec_const(parsed);
2612
- self.code.emit_u16(Opcode::LoadConst, const_idx);
2613
- Ok(conv_bits | 0x04) // has format spec on stack
2614
- }
2615
- Some(FormatSpec::Dynamic(dynamic_parts)) => {
2616
- // Compile dynamic format spec parts to build a format spec string
2617
- // Then parse it at runtime
2618
- let part_count = self.compile_fstring_parts(dynamic_parts)?;
2619
- if part_count > 1 {
2620
- self.code.emit_u16(Opcode::BuildFString, part_count);
2621
- }
2622
- // Format spec string is now on stack
2623
- Ok(conv_bits | 0x04) // has format spec on stack
2624
- }
2625
- }
2626
- }
2627
-
2628
- /// Adds a format spec to the constant pool as an encoded integer.
2629
- ///
2630
- /// Uses the encoding from `fstring::encode_format_spec` and stores it as
2631
- /// a negative integer to distinguish from regular ints.
2632
- fn add_format_spec_const(&mut self, spec: &ParsedFormatSpec) -> u16 {
2633
- let encoded = encode_format_spec(spec);
2634
- // Use negative to distinguish from regular ints (format spec marker)
2635
- // We negate and subtract 1 to ensure it's negative and recoverable
2636
- let encoded_i64 = i64::try_from(encoded).expect("format spec encoding exceeds i64::MAX");
2637
- let marker = -(encoded_i64 + 1);
2638
- self.code.add_const(Value::Int(marker))
2639
- }
2640
-
2641
- // ========================================================================
2642
- // Exception Handling Compilation
2643
- // ========================================================================
2644
-
2645
- /// Compiles a return statement, handling finally blocks properly.
2646
- ///
2647
- /// If we're inside a try-finally block, the return value is kept on the stack
2648
- /// and we jump to a "finally with return" section that runs finally then returns.
2649
- /// Otherwise, we emit a direct `ReturnValue`.
2650
- fn compile_return(&mut self) {
2651
- if let Some(finally_target) = self.finally_targets.last_mut() {
2652
- // Inside a try-finally: jump to finally, then return
2653
- // Return value is already on stack
2654
- let jump = self.code.emit_jump(Opcode::Jump);
2655
- finally_target.return_jumps.push(jump);
2656
- } else {
2657
- // Normal return
2658
- self.code.emit(Opcode::ReturnValue);
2659
- }
2660
- }
2661
-
2662
- /// Compiles a try/except/else/finally block.
2663
- ///
2664
- /// The bytecode structure is:
2665
- /// ```text
2666
- /// <try_body> # protected range
2667
- /// JUMP to_else_or_finally # skip handlers if no exception
2668
- /// handler_dispatch: # exception pushed by VM
2669
- /// # for each handler:
2670
- /// <check exception type>
2671
- /// <handler body>
2672
- /// CLEAR_EXCEPTION
2673
- /// JUMP to_finally
2674
- /// reraise:
2675
- /// RERAISE # no handler matched
2676
- /// else_block:
2677
- /// <else_body>
2678
- /// finally_block:
2679
- /// <finally_body>
2680
- /// end:
2681
- /// ```
2682
- ///
2683
- /// For finally blocks, exceptions that propagate through the handler dispatch
2684
- /// (including RERAISE when no handler matches) are caught by a second exception
2685
- /// entry that ensures finally runs before propagation.
2686
- ///
2687
- /// Returns inside try/except/else jump to a "finally with return" path that
2688
- /// runs the finally code then returns the value.
2689
- ///
2690
- /// **Note:** The finally block code is emitted multiple times (once for each
2691
- /// control flow path: normal, exception, return, break, continue). This is the
2692
- /// same approach CPython uses - each path has different stack state at entry
2693
- /// (e.g., return has a value on stack, break has popped the iterator), so we
2694
- /// can't easily share a single copy. The duplication is intentional.
2695
- fn compile_try(&mut self, try_block: &Try<PreparedNode>) -> Result<(), CompileError> {
2696
- let has_finally = !try_block.finally.is_empty();
2697
- let has_handlers = !try_block.handlers.is_empty();
2698
- let has_else = !try_block.or_else.is_empty();
2699
-
2700
- // Record stack depth at try entry (for unwinding on exception)
2701
- let stack_depth = self.code.stack_depth();
2702
-
2703
- // If there's a finally block, track returns/break/continue inside try/handlers/else
2704
- if has_finally {
2705
- self.finally_targets.push(FinallyTarget {
2706
- return_jumps: Vec::new(),
2707
- break_jumps: Vec::new(),
2708
- continue_jumps: Vec::new(),
2709
- loop_depth_at_entry: self.loop_stack.len(),
2710
- });
2711
- }
2712
-
2713
- // === Compile try body ===
2714
- let try_start = self.code.current_offset();
2715
- self.compile_block(&try_block.body)?;
2716
- let try_end = self.code.current_offset();
2717
-
2718
- // Jump to else/finally if no exception (skip handlers)
2719
- let after_try_jump = self.code.emit_jump(Opcode::Jump);
2720
-
2721
- // === Handler dispatch starts here ===
2722
- let handler_start = self.code.current_offset();
2723
-
2724
- // VM pushes exception onto stack when entering handler.
2725
- // Adjust compiler's stack depth tracking to reflect this.
2726
- self.code.adjust_stack_depth(1);
2727
-
2728
- // Track jumps that go to finally (for patching later)
2729
- let mut finally_jumps: Vec<JumpLabel> = Vec::new();
2730
-
2731
- if has_handlers {
2732
- // Compile exception handlers
2733
- // handler_entry_depth = stack_depth + 1 (exception on stack)
2734
- let handler_entry_depth = stack_depth + 1;
2735
- self.compile_exception_handlers(&try_block.handlers, &mut finally_jumps, handler_entry_depth)?;
2736
- } else {
2737
- // No handlers - just reraise (this only happens with try-finally)
2738
- self.code.emit(Opcode::Reraise);
2739
- }
2740
-
2741
- // After handler dispatch, each handler path either:
2742
- // 1. Matched and popped the exception (via Pop), then jumped to finally
2743
- // 2. Didn't match and reraised (for last handler)
2744
- // The handlers' Pop instructions already account for the exception,
2745
- // so no additional stack depth adjustment is needed here.
2746
-
2747
- // Mark end of handler dispatch (for finally exception entry)
2748
- let handler_dispatch_end = self.code.current_offset();
2749
-
2750
- // === Finally cleanup handler (for exceptions during handler dispatch) ===
2751
- // This catches exceptions from RERAISE (and any other exceptions in handlers)
2752
- // and ensures finally runs before the exception propagates.
2753
- let finally_cleanup_start = if has_finally {
2754
- let cleanup_start = self.code.current_offset();
2755
- // Exception value is on stack (pushed by VM), so stack = stack_depth + 1
2756
- self.code.set_stack_depth(stack_depth + 1);
2757
- // We need to pop it, run finally, then reraise
2758
- // But we can't easily save the exception, so we use a different approach:
2759
- // The exception is already on the exception_stack from handle_exception,
2760
- // so we can just pop from operand stack, run finally, then reraise.
2761
- self.code.emit(Opcode::Pop); // Pop exception from operand stack
2762
- self.compile_block(&try_block.finally)?;
2763
- self.code.emit(Opcode::Reraise); // Re-raise from exception_stack
2764
- Some(cleanup_start)
2765
- } else {
2766
- None
2767
- };
2768
-
2769
- // === Finally with return/break/continue paths ===
2770
- // Pop finally target and get all the jumps that need to go through finally
2771
- let finally_with_return_start = if has_finally {
2772
- let finally_target = self.finally_targets.pop().expect("finally_targets should not be empty");
2773
-
2774
- // === Finally with return path ===
2775
- let return_start = if finally_target.return_jumps.is_empty() {
2776
- None
2777
- } else {
2778
- let start = self.code.current_offset();
2779
- for jump in finally_target.return_jumps {
2780
- self.code.patch_jump(jump);
2781
- }
2782
- // Return value is on stack, stack = stack_depth + 1
2783
- self.code.set_stack_depth(stack_depth + 1);
2784
- self.compile_block(&try_block.finally)?;
2785
- self.compile_return();
2786
- Some(start)
2787
- };
2788
-
2789
- // === Finally with break path ===
2790
- // For each break, run finally then either:
2791
- // - Jump to outer finally's break path (if there's an outer finally between us and the loop)
2792
- // - Jump directly to the loop's break target
2793
- if !finally_target.break_jumps.is_empty() {
2794
- for break_info in &finally_target.break_jumps {
2795
- self.code.patch_jump(break_info.jump);
2796
- }
2797
- // Break already popped the iterator, so stack = stack_depth - 1
2798
- // (the iterator was on stack at try entry, break removed it)
2799
- self.code.set_stack_depth(stack_depth.saturating_sub(1));
2800
- self.compile_block(&try_block.finally)?;
2801
- // After finally, compile the break again (handles nested finally or direct jump)
2802
- self.compile_control_flow_after_finally(&finally_target.break_jumps, true);
2803
- }
2804
-
2805
- // === Finally with continue path ===
2806
- if !finally_target.continue_jumps.is_empty() {
2807
- for continue_info in &finally_target.continue_jumps {
2808
- self.code.patch_jump(continue_info.jump);
2809
- }
2810
- // Continue doesn't pop the iterator, stack = stack_depth
2811
- self.code.set_stack_depth(stack_depth);
2812
- self.compile_block(&try_block.finally)?;
2813
- // After finally, compile the continue again (handles nested finally or direct jump)
2814
- self.compile_control_flow_after_finally(&finally_target.continue_jumps, false);
2815
- }
2816
-
2817
- return_start
2818
- } else {
2819
- None
2820
- };
2821
-
2822
- // === Else block (runs if no exception) ===
2823
- self.code.patch_jump(after_try_jump);
2824
- // Normal path from try body, stack = stack_depth
2825
- self.code.set_stack_depth(stack_depth);
2826
- let else_start = self.code.current_offset();
2827
- if has_else {
2828
- self.compile_block(&try_block.or_else)?;
2829
- }
2830
- let else_end = self.code.current_offset();
2831
-
2832
- // === Normal finally path (no exception pending, no return) ===
2833
- // Patch all jumps from handlers to go here
2834
- for jump in finally_jumps {
2835
- self.code.patch_jump(jump);
2836
- }
2837
-
2838
- if has_finally {
2839
- // Stack = stack_depth (no exception, no return value)
2840
- self.code.set_stack_depth(stack_depth);
2841
- self.compile_block(&try_block.finally)?;
2842
- }
2843
-
2844
- // === Add exception table entries ===
2845
- // Order matters: entries are searched in order, so inner entries must come first.
2846
-
2847
- // Entry 1: Try body -> handler dispatch
2848
- if has_handlers || has_finally {
2849
- self.code.add_exception_entry(ExceptionEntry::new(
2850
- u32::try_from(try_start).expect("bytecode offset exceeds u32"),
2851
- u32::try_from(try_end).expect("bytecode offset exceeds u32") + 3, // +3 to include the JUMP instruction
2852
- u32::try_from(handler_start).expect("bytecode offset exceeds u32"),
2853
- stack_depth,
2854
- ));
2855
- }
2856
-
2857
- // Entry 2: Handler dispatch -> finally cleanup (only if has_finally)
2858
- // This ensures finally runs when RERAISE is executed or any exception occurs in handlers
2859
- if let Some(cleanup_start) = finally_cleanup_start {
2860
- self.code.add_exception_entry(ExceptionEntry::new(
2861
- u32::try_from(handler_start).expect("bytecode offset exceeds u32"),
2862
- u32::try_from(handler_dispatch_end).expect("bytecode offset exceeds u32"),
2863
- u32::try_from(cleanup_start).expect("bytecode offset exceeds u32"),
2864
- stack_depth,
2865
- ));
2866
- }
2867
-
2868
- // Entry 3: Finally with return -> finally cleanup
2869
- // If an exception occurs while running finally (in the return path), catch it
2870
- if let (Some(return_start), Some(cleanup_start)) = (finally_with_return_start, finally_cleanup_start) {
2871
- self.code.add_exception_entry(ExceptionEntry::new(
2872
- u32::try_from(return_start).expect("bytecode offset exceeds u32"),
2873
- u32::try_from(else_start).expect("bytecode offset exceeds u32"), // End at else_start (before else block)
2874
- u32::try_from(cleanup_start).expect("bytecode offset exceeds u32"),
2875
- stack_depth,
2876
- ));
2877
- }
2878
-
2879
- // Entry 4: Else block -> finally cleanup (only if has_finally and has_else)
2880
- // Exceptions in else block should go through finally
2881
- if has_else && let Some(cleanup_start) = finally_cleanup_start {
2882
- self.code.add_exception_entry(ExceptionEntry::new(
2883
- u32::try_from(else_start).expect("bytecode offset exceeds u32"),
2884
- u32::try_from(else_end).expect("bytecode offset exceeds u32"),
2885
- u32::try_from(cleanup_start).expect("bytecode offset exceeds u32"),
2886
- stack_depth,
2887
- ));
2888
- }
2889
-
2890
- Ok(())
2891
- }
2892
-
2893
- /// Compiles the exception handlers for a try block.
2894
- ///
2895
- /// Each handler checks if the exception matches its type, and if so,
2896
- /// executes the handler body. If no handler matches, the exception is re-raised.
2897
- ///
2898
- /// `handler_entry_depth` is the stack depth when entering handler dispatch
2899
- /// (i.e., base stack_depth + 1 for the exception value).
2900
- fn compile_exception_handlers(
2901
- &mut self,
2902
- handlers: &[ExceptHandler<PreparedNode>],
2903
- finally_jumps: &mut Vec<JumpLabel>,
2904
- handler_entry_depth: u16,
2905
- ) -> Result<(), CompileError> {
2906
- // Track jumps from non-matching handlers to next handler
2907
- let mut next_handler_jumps: Vec<JumpLabel> = Vec::new();
2908
-
2909
- for (i, handler) in handlers.iter().enumerate() {
2910
- let is_last = i == handlers.len() - 1;
2911
-
2912
- // Patch jumps from previous handler's non-match to here
2913
- // If jumping from a previous handler's no-match, stack has [exc, exc] (duplicate)
2914
- // We need to pop the duplicate before starting this handler's check
2915
- if !next_handler_jumps.is_empty() {
2916
- for jump in next_handler_jumps.drain(..) {
2917
- self.code.patch_jump(jump);
2918
- }
2919
- // Reset stack depth for jump target: [exc, exc] = handler_entry_depth + 1
2920
- self.code.set_stack_depth(handler_entry_depth + 1);
2921
- // Pop the duplicate from previous handler's check
2922
- self.code.emit(Opcode::Pop);
2923
- }
2924
-
2925
- if let Some(exc_type) = &handler.exc_type {
2926
- // Typed handler: except ExcType: or except ExcType as e:
2927
- // Stack: [exception]
2928
-
2929
- // Duplicate exception for type check
2930
- self.code.emit(Opcode::Dup);
2931
- // Stack: [exception, exception]
2932
-
2933
- // Load the exception type to match against
2934
- self.compile_expr(exc_type)?;
2935
- // Stack: [exception, exception, exc_type]
2936
-
2937
- // Check if exception matches the type
2938
- // This validates exc_type is a valid exception type and performs the match
2939
- // CheckExcMatch pops exc_type, peeks exception, pushes bool
2940
- self.code.emit(Opcode::CheckExcMatch);
2941
- // Stack: [exception, exception, bool]
2942
-
2943
- // Jump to next handler if match returned False
2944
- // JumpIfFalse pops the bool, leaving [exception, exception]
2945
- let no_match_jump = self.code.emit_jump(Opcode::JumpIfFalse);
2946
-
2947
- if is_last {
2948
- // Last handler - if no match, reraise
2949
- // But first we need to handle the exception var cleanup
2950
- } else {
2951
- next_handler_jumps.push(no_match_jump);
2952
- }
2953
-
2954
- // After JumpIfFalse (match succeeded), stack is [exception, exception]
2955
- // Pop the duplicate that was used for the type check
2956
- self.code.emit(Opcode::Pop);
2957
- // Stack: [exception]
2958
-
2959
- // Exception matched! Bind to variable if needed
2960
- if let Some(name) = &handler.name {
2961
- // Stack: [exception]
2962
- // Store to variable (don't pop - we still need it for current_exception)
2963
- self.code.emit(Opcode::Dup);
2964
- self.compile_store(name);
2965
- }
2966
-
2967
- // Track that we're inside an except handler (for break/continue cleanup)
2968
- self.except_handler_depth += 1;
2969
-
2970
- // Compile handler body
2971
- self.compile_block(&handler.body)?;
2972
-
2973
- // Exit except handler context
2974
- self.except_handler_depth -= 1;
2975
-
2976
- // Delete exception variable (Python 3 behavior)
2977
- if let Some(name) = &handler.name {
2978
- self.compile_delete(name);
2979
- }
2980
-
2981
- // Clear current_exception
2982
- self.code.emit(Opcode::ClearException);
2983
-
2984
- // Pop the exception from stack
2985
- self.code.emit(Opcode::Pop);
2986
-
2987
- // Jump to finally
2988
- finally_jumps.push(self.code.emit_jump(Opcode::Jump));
2989
-
2990
- // If this was last handler and no match, we need to reraise
2991
- if is_last {
2992
- self.code.patch_jump(no_match_jump);
2993
- // Coming from JumpIfFalse no-match path, stack has [exception, exception]
2994
- // Reset stack depth for jump target
2995
- self.code.set_stack_depth(handler_entry_depth + 1);
2996
- // We need to pop the duplicate before reraising
2997
- self.code.emit(Opcode::Pop);
2998
- self.code.emit(Opcode::Reraise);
2999
- }
3000
- } else {
3001
- // Bare except: catches everything
3002
- // Stack: [exception]
3003
-
3004
- // Bind to variable if needed
3005
- if let Some(name) = &handler.name {
3006
- self.code.emit(Opcode::Dup);
3007
- self.compile_store(name);
3008
- }
3009
-
3010
- // Track that we're inside an except handler (for break/continue cleanup)
3011
- self.except_handler_depth += 1;
3012
-
3013
- // Compile handler body
3014
- self.compile_block(&handler.body)?;
3015
-
3016
- // Exit except handler context
3017
- self.except_handler_depth -= 1;
3018
-
3019
- // Delete exception variable
3020
- if let Some(name) = &handler.name {
3021
- self.compile_delete(name);
3022
- }
3023
-
3024
- // Clear current_exception
3025
- self.code.emit(Opcode::ClearException);
3026
-
3027
- // Pop the exception from stack
3028
- self.code.emit(Opcode::Pop);
3029
-
3030
- // Jump to finally
3031
- finally_jumps.push(self.code.emit_jump(Opcode::Jump));
3032
- }
3033
- }
3034
-
3035
- Ok(())
3036
- }
3037
-
3038
- /// Compiles deletion of a variable.
3039
- ///
3040
- /// At module level, `Local` and `LocalUnassigned` scopes emit `DeleteGlobal`
3041
- /// because module-level locals live in the globals array.
3042
- fn compile_delete(&mut self, target: &Identifier) {
3043
- let slot = u16::try_from(target.namespace_id().index()).expect("local slot exceeds u16");
3044
- match target.scope {
3045
- NameScope::Local | NameScope::LocalUnassigned => {
3046
- if self.is_module_scope {
3047
- self.code.emit_u16(Opcode::DeleteGlobal, slot);
3048
- } else if let Ok(s) = u8::try_from(slot) {
3049
- self.code.emit_u8(Opcode::DeleteLocal, s);
3050
- } else {
3051
- // Wide variant not implemented yet
3052
- todo!("DeleteLocalW for slot > 255");
3053
- }
3054
- }
3055
- NameScope::Global => {
3056
- self.code.emit_u16(Opcode::DeleteGlobal, slot);
3057
- }
3058
- NameScope::Cell => {
3059
- // Delete cell not commonly needed
3060
- // For now, just store None
3061
- self.code.emit(Opcode::LoadNone);
3062
- self.compile_store(target);
3063
- }
3064
- }
3065
- }
3066
- }
3067
-
3068
- /// Error that can occur during bytecode compilation.
3069
- ///
3070
- /// These are typically limit violations that can't be represented in the bytecode
3071
- /// format (e.g., too many arguments, too many local variables), or import errors
3072
- /// detected at compile time.
3073
- #[derive(Debug, Clone)]
3074
- pub struct CompileError {
3075
- /// Error message describing the issue.
3076
- message: Cow<'static, str>,
3077
- /// Source location where the error occurred.
3078
- position: CodeRange,
3079
- /// Exception type to use (defaults to SyntaxError).
3080
- exc_type: ExcType,
3081
- }
3082
-
3083
- impl CompileError {
3084
- /// Creates a new compile error with the given message and position.
3085
- ///
3086
- /// Defaults to `SyntaxError` exception type.
3087
- fn new(message: impl Into<Cow<'static, str>>, position: CodeRange) -> Self {
3088
- Self {
3089
- message: message.into(),
3090
- position,
3091
- exc_type: ExcType::SyntaxError,
3092
- }
3093
- }
3094
-
3095
- /// Converts this compile error into a Python exception.
3096
- ///
3097
- /// Uses the stored exception type (SyntaxError or ModuleNotFoundError).
3098
- /// - SyntaxError: hides the `, in <module>` part (CPython's format)
3099
- /// - ModuleNotFoundError: hides caret markers (CPython doesn't show them)
3100
- pub fn into_python_exc(self, filename: &str, source: &str) -> MontyException {
3101
- let mut frame = if self.exc_type == ExcType::SyntaxError {
3102
- // SyntaxError uses different format: no `, in <module>`
3103
- StackFrame::from_position_syntax_error(self.position, filename, source)
3104
- } else {
3105
- StackFrame::from_position(self.position, filename, source)
3106
- };
3107
- // CPython doesn't show carets for module not found errors
3108
- if self.exc_type == ExcType::ModuleNotFoundError {
3109
- frame.hide_caret = true;
3110
- }
3111
- MontyException::new_full(self.exc_type, Some(self.message.into_owned()), vec![frame])
3112
- }
3113
- }
3114
-
3115
- // ============================================================================
3116
- // Operator Mapping Functions
3117
- // ============================================================================
3118
-
3119
- /// Maps a binary `Operator` to its corresponding `Opcode`.
3120
- fn operator_to_opcode(op: &Operator) -> Opcode {
3121
- match op {
3122
- Operator::Add => Opcode::BinaryAdd,
3123
- Operator::Sub => Opcode::BinarySub,
3124
- Operator::Mult => Opcode::BinaryMul,
3125
- Operator::Div => Opcode::BinaryDiv,
3126
- Operator::FloorDiv => Opcode::BinaryFloorDiv,
3127
- Operator::Mod => Opcode::BinaryMod,
3128
- Operator::Pow => Opcode::BinaryPow,
3129
- Operator::MatMult => Opcode::BinaryMatMul,
3130
- Operator::LShift => Opcode::BinaryLShift,
3131
- Operator::RShift => Opcode::BinaryRShift,
3132
- Operator::BitOr => Opcode::BinaryOr,
3133
- Operator::BitXor => Opcode::BinaryXor,
3134
- Operator::BitAnd => Opcode::BinaryAnd,
3135
- // And/Or are handled separately for short-circuit evaluation
3136
- Operator::And | Operator::Or => {
3137
- unreachable!("And/Or operators handled in compile_binary_op")
3138
- }
3139
- }
3140
- }
3141
-
3142
- /// Maps an `Operator` to its in-place (augmented assignment) `Opcode`.
3143
- ///
3144
- /// Returns `None` for operators that don't have an in-place opcode (currently `MatMult`,
3145
- /// since matrix multiplication is not yet supported). Returns `Some(opcode)` for all
3146
- /// other valid augmented assignment operators.
3147
- ///
3148
- /// # Panics
3149
- ///
3150
- /// Panics if called with `And` or `Or` operators, which cannot be used in augmented
3151
- /// assignments (this would be a parser bug).
3152
- fn operator_to_inplace_opcode(op: &Operator) -> Option<Opcode> {
3153
- match op {
3154
- Operator::Add => Some(Opcode::InplaceAdd),
3155
- Operator::Sub => Some(Opcode::InplaceSub),
3156
- Operator::Mult => Some(Opcode::InplaceMul),
3157
- Operator::Div => Some(Opcode::InplaceDiv),
3158
- Operator::FloorDiv => Some(Opcode::InplaceFloorDiv),
3159
- Operator::Mod => Some(Opcode::InplaceMod),
3160
- Operator::Pow => Some(Opcode::InplacePow),
3161
- Operator::BitAnd => Some(Opcode::InplaceAnd),
3162
- Operator::BitOr => Some(Opcode::InplaceOr),
3163
- Operator::BitXor => Some(Opcode::InplaceXor),
3164
- Operator::LShift => Some(Opcode::InplaceLShift),
3165
- Operator::RShift => Some(Opcode::InplaceRShift),
3166
- Operator::MatMult => None,
3167
- Operator::And | Operator::Or => {
3168
- unreachable!("And/Or operators cannot be used in augmented assignment")
3169
- }
3170
- }
3171
- }
3172
-
3173
- /// Maps a `CmpOperator` to its corresponding `Opcode`.
3174
- fn cmp_operator_to_opcode(op: &CmpOperator) -> Opcode {
3175
- match op {
3176
- CmpOperator::Eq => Opcode::CompareEq,
3177
- CmpOperator::NotEq => Opcode::CompareNe,
3178
- CmpOperator::Lt => Opcode::CompareLt,
3179
- CmpOperator::LtE => Opcode::CompareLe,
3180
- CmpOperator::Gt => Opcode::CompareGt,
3181
- CmpOperator::GtE => Opcode::CompareGe,
3182
- CmpOperator::Is => Opcode::CompareIs,
3183
- CmpOperator::IsNot => Opcode::CompareIsNot,
3184
- CmpOperator::In => Opcode::CompareIn,
3185
- CmpOperator::NotIn => Opcode::CompareNotIn,
3186
- // ModEq is handled specially at the call site (needs constant operand)
3187
- CmpOperator::ModEq(_) => unreachable!("ModEq handled at call site"),
3188
- }
3189
- }
3190
-
3191
- /// Returns `true` if any item in the sequence is a PEP 448 unpack (`*expr`).
3192
- ///
3193
- /// Used to choose between the fast single-`Build*(N)` path and the generalized
3194
- /// incremental `Build*(0)` + `ListAppend`/`ListExtend` (or `SetAdd`/`SetExtend`) path.
3195
- /// Only the generalized path is needed when at least one `Unpack` variant is present.
3196
- fn has_unpack_seq(items: &[SequenceItem]) -> bool {
3197
- items.iter().any(|i| matches!(i, SequenceItem::Unpack(_)))
3198
- }
3199
-
3200
- /// Returns `true` if any item in the dict literal is a PEP 448 `**expr` unpack.
3201
- ///
3202
- /// Used to choose between the fast single-`BuildDict(N)` path and the generalized
3203
- /// incremental `BuildDict(0)` + `DictSetItem`/`DictUpdate` path.
3204
- fn has_unpack_dict(items: &[DictItem]) -> bool {
3205
- items.iter().any(|i| matches!(i, DictItem::Unpack(_)))
3206
- }