superacli 1.1.6 → 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.
- package/README.md +77 -53
- package/__tests__/azd-plugin.test.js +109 -0
- package/__tests__/config.test.js +4 -3
- package/__tests__/discover.test.js +59 -0
- package/__tests__/goose-plugin.test.js +149 -0
- package/__tests__/help-json.test.js +2 -0
- package/__tests__/openhands-plugin.test.js +106 -0
- package/__tests__/plugin-cocoindex-code-uninstall.test.js +19 -0
- package/__tests__/plugin-cocoindex-code.test.js +37 -0
- package/__tests__/plugin-install-guidance.test.js +81 -0
- package/__tests__/plugins-registry.test.js +44 -0
- package/__tests__/plugins-store.test.js +40 -5
- package/__tests__/process-adapter.test.js +50 -1
- package/__tests__/server-app.test.js +1 -0
- package/__tests__/server-routes-commands.test.js +20 -2
- package/__tests__/server-routes-plugins.test.js +130 -0
- package/__tests__/skills.test.js +26 -0
- package/__tests__/squirrelscan-plugin.test.js +129 -0
- package/__tests__/uipath-plugin.test.js +104 -0
- package/__tests__/uipathcli-plugin.test.js +95 -0
- package/cli/adapters/mcp.js +2 -0
- package/cli/adapters/process.js +49 -2
- package/cli/config.js +240 -3
- package/cli/discover.js +157 -0
- package/cli/help-json.js +16 -1
- package/cli/plugin-install-guidance.js +92 -37
- package/cli/plugins-manager.js +1 -0
- package/cli/plugins-registry.js +74 -8
- package/cli/plugins-store.js +78 -17
- package/cli/skills-mcp.js +1 -1
- package/cli/skills.js +39 -2
- package/cli/supercli.js +87 -11
- package/docs/feature-gaps.md +8 -8
- package/docs/features/azd-uipath-plugins.md +43 -0
- package/docs/features/server-plugins.md +62 -0
- package/docs/features/skills.md +9 -5
- package/docs/{supported-harnesses.md → plugins-available.md} +4 -3
- package/docs/{plugin-harness-guide.md → plugins-how-to.md} +1 -1
- package/docs/plugins.md +26 -20
- package/docs/server-plugins-usage-guide.md +182 -0
- package/docs/skills-catalog.md +12 -10
- package/package.json +1 -1
- package/plugins/agent-browser/README.md +69 -0
- package/plugins/agent-browser/plugin.json +111 -0
- package/plugins/agent-browser/skills/quickstart/SKILL.md +66 -0
- package/plugins/aider/README.md +53 -0
- package/plugins/aider/plugin.json +105 -0
- package/plugins/aider/scripts/aider-wrapper.js +243 -0
- package/plugins/aider/scripts/setup-aider.js +37 -0
- package/plugins/aider/skills/dry-run-review.md +24 -0
- package/plugins/aider/skills/model-and-provider.md +24 -0
- package/plugins/aider/skills/one-shot-edits.md +30 -0
- package/plugins/aider/skills/quickstart/SKILL.md +51 -0
- package/plugins/azd/README.md +28 -0
- package/plugins/azd/plugin.json +87 -0
- package/plugins/azd/skills/quickstart/SKILL.md +41 -0
- package/plugins/blogwatcher/README.md +3 -3
- package/plugins/boxlite/Dockerfile +9 -0
- package/plugins/boxlite/README.md +62 -0
- package/plugins/boxlite/plugin.json +201 -0
- package/plugins/boxlite/scripts/run-boxlite.js +106 -0
- package/plugins/boxlite/skills/quickstart/SKILL.md +40 -0
- package/plugins/cass/plugin.json +150 -0
- package/plugins/cass/scripts/setup-cass.js +47 -0
- package/plugins/cass/skills/quickstart/SKILL.md +46 -0
- package/plugins/clever/README.md +46 -0
- package/plugins/clever/plugin.json +119 -0
- package/plugins/clever/scripts/setup-clever.js +28 -0
- package/plugins/clever/skills/auth-and-profile.md +29 -0
- package/plugins/clever/skills/passthrough-safety.md +21 -0
- package/plugins/clever/skills/quickstart/SKILL.md +45 -0
- package/plugins/clever/skills/resource-inventory.md +24 -0
- package/plugins/clix/README.md +4 -4
- package/plugins/cocoindex-code/README.md +64 -0
- package/plugins/cocoindex-code/plugin.json +81 -0
- package/plugins/cocoindex-code/scripts/__pycache__/query.cpython-310.pyc +0 -0
- package/plugins/cocoindex-code/scripts/__pycache__/query.cpython-311.pyc +0 -0
- package/plugins/cocoindex-code/scripts/post-install.js +61 -0
- package/plugins/cocoindex-code/scripts/post-uninstall.js +25 -0
- package/plugins/cocoindex-code/scripts/query.py +88 -0
- package/plugins/cocoindex-code/scripts/run-query.js +50 -0
- package/plugins/cocoindex-code/skills/quickstart/SKILL.md +73 -0
- package/plugins/copilot/README.md +24 -0
- package/plugins/copilot/plugin.json +80 -0
- package/plugins/copilot/skills/quickstart/SKILL.md +44 -0
- package/plugins/gemini/README.md +24 -0
- package/plugins/gemini/plugin.json +98 -0
- package/plugins/gemini/skills/quickstart/SKILL.md +44 -0
- package/plugins/gifcap/plugin.json +119 -0
- package/plugins/gifcap/scripts/setup-gifcap.js +44 -0
- package/plugins/gifcap/skills/quickstart/SKILL.md +34 -0
- package/plugins/gifcap/test-record-quiet.gif +0 -0
- package/plugins/gifcap/test-record.gif +0 -0
- package/plugins/goose/README.md +36 -0
- package/plugins/goose/plugin.json +183 -0
- package/plugins/goose/skills/quickstart/SKILL.md +44 -0
- package/plugins/json-server/README.md +58 -0
- package/plugins/json-server/plugin.json +113 -0
- package/plugins/json-server/skills/quickstart/SKILL.md +57 -0
- package/plugins/lightpanda/README.md +145 -0
- package/plugins/lightpanda/package-lock.json +1375 -0
- package/plugins/lightpanda/package.json +12 -0
- package/plugins/lightpanda/plugin.json +116 -0
- package/plugins/lightpanda/scripts/lightpanda-contacts.js +494 -0
- package/plugins/lightpanda/scripts/lightpanda-generic-extract.js +403 -0
- package/plugins/lightpanda/scripts/lightpanda-wrapper.js +480 -0
- package/plugins/lightpanda/scripts/setup-lightpanda.js +39 -0
- package/plugins/lightpanda/skills/contact-discovery.md +51 -0
- package/plugins/lightpanda/skills/generic-extraction.md +66 -0
- package/plugins/lightpanda/skills/quickstart/SKILL.md +103 -0
- package/plugins/lightpanda/skills/resilient-navigation.md +42 -0
- package/plugins/monty/README.md +2 -2
- package/plugins/nullclaw/README.md +3 -3
- package/plugins/offline-ai/README.md +23 -0
- package/plugins/offline-ai/plugin.json +82 -0
- package/plugins/offline-ai/skills/quickstart/SKILL.md +43 -0
- package/plugins/openhands/README.md +25 -0
- package/plugins/openhands/plugin.json +116 -0
- package/plugins/openhands/skills/quickstart/SKILL.md +26 -0
- package/plugins/plandex/README.md +25 -0
- package/plugins/plandex/plugin.json +130 -0
- package/plugins/plandex/skills/quickstart/SKILL.md +50 -0
- package/plugins/plugins.json +188 -0
- package/plugins/squirrelscan/Dockerfile +5 -0
- package/plugins/squirrelscan/README.md +47 -0
- package/plugins/squirrelscan/plugin.json +493 -0
- package/plugins/squirrelscan/scripts/post-install.js +33 -0
- package/plugins/squirrelscan/scripts/post-uninstall.js +25 -0
- package/plugins/squirrelscan/scripts/run-squirrel.js +73 -0
- package/plugins/squirrelscan/skills/audit-workflow/SKILL.md +33 -0
- package/plugins/squirrelscan/skills/publish-report/SKILL.md +33 -0
- package/plugins/squirrelscan/skills/quickstart/SKILL.md +41 -0
- package/plugins/uipath/README.md +27 -0
- package/plugins/uipath/plugin.json +86 -0
- package/plugins/uipath/skills/quickstart/SKILL.md +47 -0
- package/plugins/uipathcli/README.md +28 -0
- package/plugins/uipathcli/plugin.json +120 -0
- package/plugins/uipathcli/scripts/run-uipath-cli.js +49 -0
- package/plugins/uipathcli/skills/quickstart/SKILL.md +22 -0
- package/plugins/xurl/README.md +4 -4
- package/server/app.js +5 -2
- package/server/public/app.js +3 -0
- package/server/routes/commands.js +95 -12
- package/server/routes/plugins.js +262 -0
- package/server/services/pluginsService.js +303 -0
- package/server/views/command-edit.ejs +196 -14
- package/server/views/partials/head.ejs +1 -0
- package/server/views/plugins.ejs +264 -0
- package/tests/test-plugins-registry.js +30 -0
- package/.beads/.br_history/issues.20260308_200823_636718328.jsonl +0 -20
- package/.beads/.br_history/issues.20260308_200823_636718328.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_200827_033159453.jsonl +0 -21
- package/.beads/.br_history/issues.20260308_200827_033159453.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_200829_595900053.jsonl +0 -22
- package/.beads/.br_history/issues.20260308_200829_595900053.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_200834_079930100.jsonl +0 -23
- package/.beads/.br_history/issues.20260308_200834_079930100.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_200858_370924996.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_200858_370924996.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201031_019730855.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_201031_019730855.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201031_578974884.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_201031_578974884.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201054_780345548.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_201054_780345548.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201054_896980019.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_201054_896980019.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201128_599819688.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_201128_599819688.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201128_710221699.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_201128_710221699.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201204_745649213.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_201204_745649213.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201338_908436144.jsonl +0 -24
- package/.beads/.br_history/issues.20260308_201338_908436144.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201344_734860714.jsonl +0 -25
- package/.beads/.br_history/issues.20260308_201344_734860714.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_201630_819282295.jsonl +0 -25
- package/.beads/.br_history/issues.20260308_201630_819282295.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_203538_054279699.jsonl +0 -25
- package/.beads/.br_history/issues.20260308_203538_054279699.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_203547_597113070.jsonl +0 -26
- package/.beads/.br_history/issues.20260308_203547_597113070.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_203547_775139216.jsonl +0 -27
- package/.beads/.br_history/issues.20260308_203547_775139216.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_203547_950724773.jsonl +0 -28
- package/.beads/.br_history/issues.20260308_203547_950724773.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_203548_107684523.jsonl +0 -29
- package/.beads/.br_history/issues.20260308_203548_107684523.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_203548_310389993.jsonl +0 -30
- package/.beads/.br_history/issues.20260308_203548_310389993.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_203825_953337320.jsonl +0 -31
- package/.beads/.br_history/issues.20260308_203825_953337320.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_204056_071377736.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_204056_071377736.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205141_517616844.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205141_517616844.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205141_648994024.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205141_648994024.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205141_867598036.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205141_867598036.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205142_094157355.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205142_094157355.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205142_327315677.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205142_327315677.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205142_545563822.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205142_545563822.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205213_061989333.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205213_061989333.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205213_181103364.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205213_181103364.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205213_408872234.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205213_408872234.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205213_616681652.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205213_616681652.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205213_821507069.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205213_821507069.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205214_026661112.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205214_026661112.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205454_955250554.jsonl +0 -32
- package/.beads/.br_history/issues.20260308_205454_955250554.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205556_337800392.jsonl +0 -33
- package/.beads/.br_history/issues.20260308_205556_337800392.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_205824_274686694.jsonl +0 -33
- package/.beads/.br_history/issues.20260308_205824_274686694.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_210240_583768328.jsonl +0 -34
- package/.beads/.br_history/issues.20260308_210240_583768328.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212223_641541494.jsonl +0 -34
- package/.beads/.br_history/issues.20260308_212223_641541494.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212227_735550996.jsonl +0 -35
- package/.beads/.br_history/issues.20260308_212227_735550996.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212232_547298548.jsonl +0 -36
- package/.beads/.br_history/issues.20260308_212232_547298548.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212528_843628125.jsonl +0 -37
- package/.beads/.br_history/issues.20260308_212528_843628125.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212529_094530502.jsonl +0 -38
- package/.beads/.br_history/issues.20260308_212529_094530502.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212529_331000853.jsonl +0 -39
- package/.beads/.br_history/issues.20260308_212529_331000853.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212529_587925652.jsonl +0 -40
- package/.beads/.br_history/issues.20260308_212529_587925652.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212804_927764103.jsonl +0 -41
- package/.beads/.br_history/issues.20260308_212804_927764103.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212805_153673453.jsonl +0 -42
- package/.beads/.br_history/issues.20260308_212805_153673453.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212805_415982363.jsonl +0 -43
- package/.beads/.br_history/issues.20260308_212805_415982363.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212805_657497741.jsonl +0 -44
- package/.beads/.br_history/issues.20260308_212805_657497741.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212805_952838724.jsonl +0 -45
- package/.beads/.br_history/issues.20260308_212805_952838724.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212806_325433779.jsonl +0 -46
- package/.beads/.br_history/issues.20260308_212806_325433779.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212806_584685598.jsonl +0 -47
- package/.beads/.br_history/issues.20260308_212806_584685598.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212806_827817208.jsonl +0 -48
- package/.beads/.br_history/issues.20260308_212806_827817208.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212807_111320451.jsonl +0 -49
- package/.beads/.br_history/issues.20260308_212807_111320451.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212807_409545536.jsonl +0 -50
- package/.beads/.br_history/issues.20260308_212807_409545536.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212807_625063294.jsonl +0 -51
- package/.beads/.br_history/issues.20260308_212807_625063294.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212807_843906551.jsonl +0 -52
- package/.beads/.br_history/issues.20260308_212807_843906551.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212808_100304073.jsonl +0 -53
- package/.beads/.br_history/issues.20260308_212808_100304073.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212808_324723976.jsonl +0 -54
- package/.beads/.br_history/issues.20260308_212808_324723976.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212808_557513104.jsonl +0 -55
- package/.beads/.br_history/issues.20260308_212808_557513104.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_212808_788048322.jsonl +0 -56
- package/.beads/.br_history/issues.20260308_212808_788048322.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_213702_613249728.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_213702_613249728.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_213715_115792063.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_213715_115792063.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_213715_462220666.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_213715_462220666.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_213727_191258923.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_213727_191258923.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_213727_684383652.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_213727_684383652.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_213735_751882991.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_213735_751882991.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_222052_279844960.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_222052_279844960.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_222056_873282114.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_222056_873282114.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_222103_402410761.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_222103_402410761.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235202_180577215.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235202_180577215.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235202_387414163.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235202_387414163.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235202_564422794.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235202_564422794.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235202_742600597.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235202_742600597.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235208_133360069.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235208_133360069.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235505_473406307.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235505_473406307.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235505_662360489.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235505_662360489.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235505_843935624.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235505_843935624.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260308_235506_044530221.jsonl +0 -57
- package/.beads/.br_history/issues.20260308_235506_044530221.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_002618_115728731.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_002618_115728731.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_003748_878174586.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_003748_878174586.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_004057_868755623.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_004057_868755623.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_004058_512842163.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_004058_512842163.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_004058_994445226.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_004058_994445226.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_004059_475988596.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_004059_475988596.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_161902_566857851.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_161902_566857851.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_170512_277017739.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_170512_277017739.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_170512_477876921.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_170512_477876921.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_170512_664382701.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_170512_664382701.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_170512_859400333.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_170512_859400333.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_212326_082771164.jsonl +0 -57
- package/.beads/.br_history/issues.20260309_212326_082771164.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_212326_245619716.jsonl +0 -58
- package/.beads/.br_history/issues.20260309_212326_245619716.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_212326_403198317.jsonl +0 -59
- package/.beads/.br_history/issues.20260309_212326_403198317.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_212332_539197678.jsonl +0 -60
- package/.beads/.br_history/issues.20260309_212332_539197678.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_212332_731373599.jsonl +0 -60
- package/.beads/.br_history/issues.20260309_212332_731373599.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_212332_928710953.jsonl +0 -60
- package/.beads/.br_history/issues.20260309_212332_928710953.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_213021_341505240.jsonl +0 -60
- package/.beads/.br_history/issues.20260309_213021_341505240.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_213022_023136934.jsonl +0 -60
- package/.beads/.br_history/issues.20260309_213022_023136934.jsonl.meta.json +0 -1
- package/.beads/.br_history/issues.20260309_213022_400050719.jsonl +0 -60
- package/.beads/.br_history/issues.20260309_213022_400050719.jsonl.meta.json +0 -1
- package/.beads/config.yaml +0 -4
- package/.beads/issues.jsonl +0 -60
- package/.beads/metadata.json +0 -4
- package/docs/mcp-cheatsheet.md +0 -324
- package/docs/visual-overview.md +0 -21
- package/ref-monty/.cargo/config.toml +0 -3
- package/ref-monty/.claude/settings.json +0 -60
- package/ref-monty/.claude/skills/fastmod/SKILL.md +0 -22
- package/ref-monty/.claude/skills/python-playground/SKILL.md +0 -47
- package/ref-monty/.codecov.yml +0 -12
- package/ref-monty/.github/actions/build-pgo-wheel/action.yml +0 -72
- package/ref-monty/.github/workflows/ci.yml +0 -776
- package/ref-monty/.github/workflows/codspeed.yml +0 -45
- package/ref-monty/.github/workflows/init-npm-packages.yml +0 -82
- package/ref-monty/.pre-commit-config.yaml +0 -47
- package/ref-monty/.python-version +0 -1
- package/ref-monty/.rustfmt.toml +0 -4
- package/ref-monty/.zed/settings.json +0 -11
- package/ref-monty/CLAUDE.md +0 -535
- package/ref-monty/Cargo.lock +0 -3798
- package/ref-monty/Cargo.toml +0 -87
- package/ref-monty/LICENSE +0 -21
- package/ref-monty/Makefile +0 -216
- package/ref-monty/README.md +0 -430
- package/ref-monty/RELEASING.md +0 -47
- package/ref-monty/crates/fuzz/Cargo.toml +0 -30
- package/ref-monty/crates/fuzz/fuzz_targets/string_input_panic.rs +0 -37
- package/ref-monty/crates/fuzz/fuzz_targets/tokens_input_panic.rs +0 -552
- package/ref-monty/crates/monty/Cargo.toml +0 -68
- package/ref-monty/crates/monty/benches/main.rs +0 -247
- package/ref-monty/crates/monty/build.rs +0 -10
- package/ref-monty/crates/monty/src/args.rs +0 -733
- package/ref-monty/crates/monty/src/asyncio.rs +0 -179
- package/ref-monty/crates/monty/src/builtins/abs.rs +0 -55
- package/ref-monty/crates/monty/src/builtins/all.rs +0 -30
- package/ref-monty/crates/monty/src/builtins/any.rs +0 -30
- package/ref-monty/crates/monty/src/builtins/bin.rs +0 -59
- package/ref-monty/crates/monty/src/builtins/chr.rs +0 -46
- package/ref-monty/crates/monty/src/builtins/divmod.rs +0 -164
- package/ref-monty/crates/monty/src/builtins/enumerate.rs +0 -52
- package/ref-monty/crates/monty/src/builtins/filter.rs +0 -67
- package/ref-monty/crates/monty/src/builtins/getattr.rs +0 -65
- package/ref-monty/crates/monty/src/builtins/hash.rs +0 -28
- package/ref-monty/crates/monty/src/builtins/hex.rs +0 -58
- package/ref-monty/crates/monty/src/builtins/id.rs +0 -24
- package/ref-monty/crates/monty/src/builtins/isinstance.rs +0 -68
- package/ref-monty/crates/monty/src/builtins/len.rs +0 -25
- package/ref-monty/crates/monty/src/builtins/map.rs +0 -98
- package/ref-monty/crates/monty/src/builtins/min_max.rs +0 -113
- package/ref-monty/crates/monty/src/builtins/mod.rs +0 -246
- package/ref-monty/crates/monty/src/builtins/next.rs +0 -21
- package/ref-monty/crates/monty/src/builtins/oct.rs +0 -59
- package/ref-monty/crates/monty/src/builtins/ord.rs +0 -67
- package/ref-monty/crates/monty/src/builtins/pow.rs +0 -365
- package/ref-monty/crates/monty/src/builtins/print.rs +0 -141
- package/ref-monty/crates/monty/src/builtins/repr.rs +0 -16
- package/ref-monty/crates/monty/src/builtins/reversed.rs +0 -28
- package/ref-monty/crates/monty/src/builtins/round.rs +0 -174
- package/ref-monty/crates/monty/src/builtins/sorted.rs +0 -151
- package/ref-monty/crates/monty/src/builtins/sum.rs +0 -66
- package/ref-monty/crates/monty/src/builtins/type_.rs +0 -16
- package/ref-monty/crates/monty/src/builtins/zip.rs +0 -77
- package/ref-monty/crates/monty/src/bytecode/builder.rs +0 -699
- package/ref-monty/crates/monty/src/bytecode/code.rs +0 -310
- package/ref-monty/crates/monty/src/bytecode/compiler.rs +0 -3206
- package/ref-monty/crates/monty/src/bytecode/mod.rs +0 -24
- package/ref-monty/crates/monty/src/bytecode/op.rs +0 -617
- package/ref-monty/crates/monty/src/bytecode/vm/async_exec.rs +0 -1058
- package/ref-monty/crates/monty/src/bytecode/vm/attr.rs +0 -63
- package/ref-monty/crates/monty/src/bytecode/vm/binary.rs +0 -487
- package/ref-monty/crates/monty/src/bytecode/vm/call.rs +0 -767
- package/ref-monty/crates/monty/src/bytecode/vm/collections.rs +0 -741
- package/ref-monty/crates/monty/src/bytecode/vm/compare.rs +0 -147
- package/ref-monty/crates/monty/src/bytecode/vm/exceptions.rs +0 -297
- package/ref-monty/crates/monty/src/bytecode/vm/format.rs +0 -132
- package/ref-monty/crates/monty/src/bytecode/vm/mod.rs +0 -1958
- package/ref-monty/crates/monty/src/bytecode/vm/scheduler.rs +0 -620
- package/ref-monty/crates/monty/src/exception_private.rs +0 -1513
- package/ref-monty/crates/monty/src/exception_public.rs +0 -346
- package/ref-monty/crates/monty/src/expressions.rs +0 -694
- package/ref-monty/crates/monty/src/fstring.rs +0 -854
- package/ref-monty/crates/monty/src/function.rs +0 -119
- package/ref-monty/crates/monty/src/heap.rs +0 -1073
- package/ref-monty/crates/monty/src/heap_data.rs +0 -985
- package/ref-monty/crates/monty/src/heap_traits.rs +0 -312
- package/ref-monty/crates/monty/src/intern.rs +0 -837
- package/ref-monty/crates/monty/src/io.rs +0 -106
- package/ref-monty/crates/monty/src/lib.rs +0 -52
- package/ref-monty/crates/monty/src/modules/asyncio.rs +0 -144
- package/ref-monty/crates/monty/src/modules/math.rs +0 -1453
- package/ref-monty/crates/monty/src/modules/mod.rs +0 -120
- package/ref-monty/crates/monty/src/modules/os.rs +0 -116
- package/ref-monty/crates/monty/src/modules/pathlib.rs +0 -33
- package/ref-monty/crates/monty/src/modules/re.rs +0 -606
- package/ref-monty/crates/monty/src/modules/sys.rs +0 -60
- package/ref-monty/crates/monty/src/modules/typing.rs +0 -70
- package/ref-monty/crates/monty/src/namespace.rs +0 -21
- package/ref-monty/crates/monty/src/object.rs +0 -1040
- package/ref-monty/crates/monty/src/os.rs +0 -215
- package/ref-monty/crates/monty/src/parse.rs +0 -1730
- package/ref-monty/crates/monty/src/prepare.rs +0 -3015
- package/ref-monty/crates/monty/src/repl.rs +0 -1109
- package/ref-monty/crates/monty/src/resource.rs +0 -559
- package/ref-monty/crates/monty/src/run.rs +0 -457
- package/ref-monty/crates/monty/src/run_progress.rs +0 -821
- package/ref-monty/crates/monty/src/signature.rs +0 -651
- package/ref-monty/crates/monty/src/sorting.rs +0 -100
- package/ref-monty/crates/monty/src/types/bytes.rs +0 -2356
- package/ref-monty/crates/monty/src/types/dataclass.rs +0 -345
- package/ref-monty/crates/monty/src/types/dict.rs +0 -879
- package/ref-monty/crates/monty/src/types/dict_view.rs +0 -619
- package/ref-monty/crates/monty/src/types/iter.rs +0 -799
- package/ref-monty/crates/monty/src/types/list.rs +0 -929
- package/ref-monty/crates/monty/src/types/long_int.rs +0 -211
- package/ref-monty/crates/monty/src/types/mod.rs +0 -48
- package/ref-monty/crates/monty/src/types/module.rs +0 -146
- package/ref-monty/crates/monty/src/types/namedtuple.rs +0 -261
- package/ref-monty/crates/monty/src/types/path.rs +0 -596
- package/ref-monty/crates/monty/src/types/property.rs +0 -35
- package/ref-monty/crates/monty/src/types/py_trait.rs +0 -322
- package/ref-monty/crates/monty/src/types/range.rs +0 -285
- package/ref-monty/crates/monty/src/types/re_match.rs +0 -522
- package/ref-monty/crates/monty/src/types/re_pattern.rs +0 -726
- package/ref-monty/crates/monty/src/types/set.rs +0 -1373
- package/ref-monty/crates/monty/src/types/slice.rs +0 -257
- package/ref-monty/crates/monty/src/types/str.rs +0 -2051
- package/ref-monty/crates/monty/src/types/tuple.rs +0 -376
- package/ref-monty/crates/monty/src/types/type.rs +0 -407
- package/ref-monty/crates/monty/src/value.rs +0 -2558
- package/ref-monty/crates/monty/test_cases/args__dict_get_no_args.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__dict_get_too_many.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__dict_items_with_args.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__dict_keys_with_args.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__dict_pop_no_args.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__dict_pop_too_many.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__dict_values_with_args.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__id_too_many.py +0 -2
- package/ref-monty/crates/monty/test_cases/args__len_no_args.py +0 -2
- package/ref-monty/crates/monty/test_cases/args__len_too_many.py +0 -2
- package/ref-monty/crates/monty/test_cases/args__len_type_error_int.py +0 -9
- package/ref-monty/crates/monty/test_cases/args__len_type_error_none.py +0 -9
- package/ref-monty/crates/monty/test_cases/args__list_append_no_args.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__list_append_too_many.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__list_insert_too_few.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__list_insert_too_many.py +0 -3
- package/ref-monty/crates/monty/test_cases/args__repr_no_args.py +0 -2
- package/ref-monty/crates/monty/test_cases/arith__div_zero_float.py +0 -2
- package/ref-monty/crates/monty/test_cases/arith__div_zero_int.py +0 -2
- package/ref-monty/crates/monty/test_cases/arith__floordiv_zero_float.py +0 -2
- package/ref-monty/crates/monty/test_cases/arith__floordiv_zero_int.py +0 -2
- package/ref-monty/crates/monty/test_cases/arith__pow_zero_neg.py +0 -2
- package/ref-monty/crates/monty/test_cases/arith__pow_zero_neg_builtin.py +0 -9
- package/ref-monty/crates/monty/test_cases/assert__expr_fail.py +0 -2
- package/ref-monty/crates/monty/test_cases/assert__fail.py +0 -2
- package/ref-monty/crates/monty/test_cases/assert__fail_msg.py +0 -2
- package/ref-monty/crates/monty/test_cases/assert__fn_fail.py +0 -3
- package/ref-monty/crates/monty/test_cases/assert__ops.py +0 -11
- package/ref-monty/crates/monty/test_cases/async__asyncio_run.py +0 -47
- package/ref-monty/crates/monty/test_cases/async__basic.py +0 -10
- package/ref-monty/crates/monty/test_cases/async__closure.py +0 -14
- package/ref-monty/crates/monty/test_cases/async__double_await_coroutine.py +0 -16
- package/ref-monty/crates/monty/test_cases/async__exception.py +0 -10
- package/ref-monty/crates/monty/test_cases/async__ext_call.py +0 -73
- package/ref-monty/crates/monty/test_cases/async__gather_all.py +0 -85
- package/ref-monty/crates/monty/test_cases/async__nested_await.py +0 -15
- package/ref-monty/crates/monty/test_cases/async__nested_gather_ext.py +0 -37
- package/ref-monty/crates/monty/test_cases/async__not_awaitable.py +0 -10
- package/ref-monty/crates/monty/test_cases/async__not_imported.py +0 -14
- package/ref-monty/crates/monty/test_cases/async__recursion_depth_isolation.py +0 -27
- package/ref-monty/crates/monty/test_cases/async__return_types.py +0 -31
- package/ref-monty/crates/monty/test_cases/async__sequential.py +0 -16
- package/ref-monty/crates/monty/test_cases/async__traceback.py +0 -19
- package/ref-monty/crates/monty/test_cases/async__with_args.py +0 -14
- package/ref-monty/crates/monty/test_cases/attr__get_int_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/attr__get_list_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/attr__set_frozen_nonfield.py +0 -12
- package/ref-monty/crates/monty/test_cases/attr__set_int_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/attr__set_list_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/bench__kitchen_sink.py +0 -68
- package/ref-monty/crates/monty/test_cases/bool__ops.py +0 -20
- package/ref-monty/crates/monty/test_cases/builtin__add_type_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/builtin__filter.py +0 -62
- package/ref-monty/crates/monty/test_cases/builtin__filter_not_iterable.py +0 -11
- package/ref-monty/crates/monty/test_cases/builtin__getattr.py +0 -84
- package/ref-monty/crates/monty/test_cases/builtin__iter_funcs.py +0 -42
- package/ref-monty/crates/monty/test_cases/builtin__iter_next.py +0 -66
- package/ref-monty/crates/monty/test_cases/builtin__map.py +0 -74
- package/ref-monty/crates/monty/test_cases/builtin__map_not_iterable.py +0 -11
- package/ref-monty/crates/monty/test_cases/builtin__math_funcs.py +0 -154
- package/ref-monty/crates/monty/test_cases/builtin__more_iter_funcs.py +0 -148
- package/ref-monty/crates/monty/test_cases/builtin__next_stop_iteration.py +0 -10
- package/ref-monty/crates/monty/test_cases/builtin__print_invalid_kwarg.py +0 -9
- package/ref-monty/crates/monty/test_cases/builtin__print_kwargs.py +0 -12
- package/ref-monty/crates/monty/test_cases/builtin__repr.py +0 -3
- package/ref-monty/crates/monty/test_cases/builtin__string_funcs.py +0 -73
- package/ref-monty/crates/monty/test_cases/bytes__decode_invalid_utf8.py +0 -18
- package/ref-monty/crates/monty/test_cases/bytes__endswith_str_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/bytes__getitem_index_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/bytes__index_start_gt_end.py +0 -10
- package/ref-monty/crates/monty/test_cases/bytes__methods.py +0 -394
- package/ref-monty/crates/monty/test_cases/bytes__negative_count.py +0 -9
- package/ref-monty/crates/monty/test_cases/bytes__ops.py +0 -90
- package/ref-monty/crates/monty/test_cases/bytes__startswith_str_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/call_object.py +0 -3
- package/ref-monty/crates/monty/test_cases/chain_comparison__all.py +0 -79
- package/ref-monty/crates/monty/test_cases/closure__param_shadows_outer.py +0 -81
- package/ref-monty/crates/monty/test_cases/closure__pep448.py +0 -203
- package/ref-monty/crates/monty/test_cases/closure__undefined_nonlocal.py +0 -13
- package/ref-monty/crates/monty/test_cases/compare__mixed_types.py +0 -120
- package/ref-monty/crates/monty/test_cases/comprehension__all.py +0 -208
- package/ref-monty/crates/monty/test_cases/comprehension__scope.py +0 -7
- package/ref-monty/crates/monty/test_cases/comprehension__unbound_local.py +0 -14
- package/ref-monty/crates/monty/test_cases/dataclass__basic.py +0 -238
- package/ref-monty/crates/monty/test_cases/dataclass__call_field_error.py +0 -12
- package/ref-monty/crates/monty/test_cases/dataclass__frozen_set_error.py +0 -12
- package/ref-monty/crates/monty/test_cases/dataclass__get_missing_attr_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/dict__get_unhashable_key.py +0 -3
- package/ref-monty/crates/monty/test_cases/dict__literal_unhashable_key.py +0 -2
- package/ref-monty/crates/monty/test_cases/dict__method_pop_missing_error.py +0 -3
- package/ref-monty/crates/monty/test_cases/dict__methods.py +0 -151
- package/ref-monty/crates/monty/test_cases/dict__ops.py +0 -133
- package/ref-monty/crates/monty/test_cases/dict__pop_unhashable_key.py +0 -4
- package/ref-monty/crates/monty/test_cases/dict__popitem_empty.py +0 -9
- package/ref-monty/crates/monty/test_cases/dict__subscript_missing_key.py +0 -3
- package/ref-monty/crates/monty/test_cases/dict__unhashable_dict_key.py +0 -2
- package/ref-monty/crates/monty/test_cases/dict__unhashable_list_key.py +0 -2
- package/ref-monty/crates/monty/test_cases/dict__unpack_type_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/dict__views.py +0 -165
- package/ref-monty/crates/monty/test_cases/edge__all.py +0 -26
- package/ref-monty/crates/monty/test_cases/edge__float_int_mod.py +0 -2
- package/ref-monty/crates/monty/test_cases/edge__int_float_mod.py +0 -2
- package/ref-monty/crates/monty/test_cases/exc__args.py +0 -16
- package/ref-monty/crates/monty/test_cases/exc__str.py +0 -15
- package/ref-monty/crates/monty/test_cases/execute_ok__all.py +0 -54
- package/ref-monty/crates/monty/test_cases/execute_raise__error_instance_str.py +0 -2
- package/ref-monty/crates/monty/test_cases/execute_raise__error_no_args.py +0 -2
- package/ref-monty/crates/monty/test_cases/execute_raise__error_string_arg.py +0 -2
- package/ref-monty/crates/monty/test_cases/execute_raise__error_string_arg_quotes.py +0 -2
- package/ref-monty/crates/monty/test_cases/execute_raise__error_type.py +0 -2
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_instance_via_var.py +0 -4
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_list.py +0 -2
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_number.py +0 -2
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_type_call_via_var.py +0 -4
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_type_direct.py +0 -3
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_type_via_var.py +0 -4
- package/ref-monty/crates/monty/test_cases/ext_call__arg_side_effect_bug.py +0 -22
- package/ref-monty/crates/monty/test_cases/ext_call__augmented.py +0 -17
- package/ref-monty/crates/monty/test_cases/ext_call__augmented_refcount_bug.py +0 -7
- package/ref-monty/crates/monty/test_cases/ext_call__bare_raise_after_resume.py +0 -34
- package/ref-monty/crates/monty/test_cases/ext_call__basic.py +0 -99
- package/ref-monty/crates/monty/test_cases/ext_call__boolean.py +0 -37
- package/ref-monty/crates/monty/test_cases/ext_call__boolean_side_effect_hang.py +0 -17
- package/ref-monty/crates/monty/test_cases/ext_call__closure_bug.py +0 -16
- package/ref-monty/crates/monty/test_cases/ext_call__comparison.py +0 -26
- package/ref-monty/crates/monty/test_cases/ext_call__deep_call_stack.py +0 -18
- package/ref-monty/crates/monty/test_cases/ext_call__elif.py +0 -171
- package/ref-monty/crates/monty/test_cases/ext_call__exc.py +0 -4
- package/ref-monty/crates/monty/test_cases/ext_call__exc_deep_stack.py +0 -39
- package/ref-monty/crates/monty/test_cases/ext_call__exc_in_function.py +0 -17
- package/ref-monty/crates/monty/test_cases/ext_call__exc_nested_functions.py +0 -31
- package/ref-monty/crates/monty/test_cases/ext_call__ext_exc.py +0 -171
- package/ref-monty/crates/monty/test_cases/ext_call__for.py +0 -114
- package/ref-monty/crates/monty/test_cases/ext_call__fstring.py +0 -12
- package/ref-monty/crates/monty/test_cases/ext_call__if.py +0 -135
- package/ref-monty/crates/monty/test_cases/ext_call__if_condition.py +0 -37
- package/ref-monty/crates/monty/test_cases/ext_call__in_closure.py +0 -14
- package/ref-monty/crates/monty/test_cases/ext_call__in_function.py +0 -40
- package/ref-monty/crates/monty/test_cases/ext_call__in_function_simple.py +0 -7
- package/ref-monty/crates/monty/test_cases/ext_call__literals.py +0 -17
- package/ref-monty/crates/monty/test_cases/ext_call__multi_in_func.py +0 -32
- package/ref-monty/crates/monty/test_cases/ext_call__name_lookup.py +0 -69
- package/ref-monty/crates/monty/test_cases/ext_call__name_lookup_undefined.py +0 -4
- package/ref-monty/crates/monty/test_cases/ext_call__nested_calls.py +0 -14
- package/ref-monty/crates/monty/test_cases/ext_call__recursion_bug.py +0 -19
- package/ref-monty/crates/monty/test_cases/ext_call__return.py +0 -28
- package/ref-monty/crates/monty/test_cases/ext_call__side_effects.py +0 -25
- package/ref-monty/crates/monty/test_cases/ext_call__subscript.py +0 -7
- package/ref-monty/crates/monty/test_cases/ext_call__ternary.py +0 -28
- package/ref-monty/crates/monty/test_cases/ext_call__try.py +0 -280
- package/ref-monty/crates/monty/test_cases/ext_call__try_simple.py +0 -10
- package/ref-monty/crates/monty/test_cases/ext_call__unary.py +0 -13
- package/ref-monty/crates/monty/test_cases/frozenset__ops.py +0 -178
- package/ref-monty/crates/monty/test_cases/fstring__all.py +0 -236
- package/ref-monty/crates/monty/test_cases/fstring__error_eq_align_on_str.py +0 -3
- package/ref-monty/crates/monty/test_cases/fstring__error_float_f_on_str.py +0 -3
- package/ref-monty/crates/monty/test_cases/fstring__error_int_d_on_float.py +0 -3
- package/ref-monty/crates/monty/test_cases/fstring__error_int_d_on_str.py +0 -3
- package/ref-monty/crates/monty/test_cases/fstring__error_invalid_spec.py +0 -4
- package/ref-monty/crates/monty/test_cases/fstring__error_invalid_spec_dynamic.py +0 -4
- package/ref-monty/crates/monty/test_cases/fstring__error_invalid_spec_str.py +0 -4
- package/ref-monty/crates/monty/test_cases/fstring__error_str_s_on_int.py +0 -3
- package/ref-monty/crates/monty/test_cases/function__call_duplicate_kwargs.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__call_unpack.py +0 -42
- package/ref-monty/crates/monty/test_cases/function__defaults.py +0 -117
- package/ref-monty/crates/monty/test_cases/function__err_duplicate_arg.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_duplicate_first_arg.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_duplicate_kwarg_cleanup.py +0 -9
- package/ref-monty/crates/monty/test_cases/function__err_kwonly_as_positional.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_missing_all_posonly.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_missing_heap_cleanup.py +0 -9
- package/ref-monty/crates/monty/test_cases/function__err_missing_kwonly.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_missing_posonly_with_kwarg.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_missing_with_posonly.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_posonly_as_kwarg.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_posonly_first_as_kwarg.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_too_many_posonly.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_too_many_with_kwonly.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg_cleanup.py +0 -9
- package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg_quote.py +0 -13
- package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg_simple.py +0 -7
- package/ref-monty/crates/monty/test_cases/function__err_unpack_duplicate_arg.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__err_unpack_duplicate_heap.py +0 -8
- package/ref-monty/crates/monty/test_cases/function__err_unpack_int.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__err_unpack_nonstring_key.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__err_unpack_not_mapping.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__kwargs_unpacking.py +0 -173
- package/ref-monty/crates/monty/test_cases/function__ops.py +0 -294
- package/ref-monty/crates/monty/test_cases/function__return_none.py +0 -42
- package/ref-monty/crates/monty/test_cases/function__signatures.py +0 -47
- package/ref-monty/crates/monty/test_cases/function__too_few_args_all.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__too_few_args_one.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__too_few_args_two.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__too_many_args_one.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__too_many_args_two.py +0 -6
- package/ref-monty/crates/monty/test_cases/function__too_many_args_zero.py +0 -6
- package/ref-monty/crates/monty/test_cases/global__error_assigned_before.py +0 -7
- package/ref-monty/crates/monty/test_cases/global__ops.py +0 -163
- package/ref-monty/crates/monty/test_cases/hash__dict_unhashable.py +0 -2
- package/ref-monty/crates/monty/test_cases/hash__list_unhashable.py +0 -2
- package/ref-monty/crates/monty/test_cases/hash__ops.py +0 -153
- package/ref-monty/crates/monty/test_cases/id__bytes_literals_distinct.py +0 -3
- package/ref-monty/crates/monty/test_cases/id__int_copy_distinct.py +0 -5
- package/ref-monty/crates/monty/test_cases/id__is_number_is_number.py +0 -3
- package/ref-monty/crates/monty/test_cases/id__non_overlapping_lifetimes_distinct_types.py +0 -10
- package/ref-monty/crates/monty/test_cases/id__non_overlapping_lifetimes_same_types.py +0 -6
- package/ref-monty/crates/monty/test_cases/id__ops.py +0 -97
- package/ref-monty/crates/monty/test_cases/id__str_literals_same.py +0 -3
- package/ref-monty/crates/monty/test_cases/if__elif_else.py +0 -207
- package/ref-monty/crates/monty/test_cases/if__raise_elif.py +0 -11
- package/ref-monty/crates/monty/test_cases/if__raise_else.py +0 -13
- package/ref-monty/crates/monty/test_cases/if__raise_if.py +0 -9
- package/ref-monty/crates/monty/test_cases/if__raise_in_elif_condition.py +0 -18
- package/ref-monty/crates/monty/test_cases/if__raise_in_if_condition.py +0 -16
- package/ref-monty/crates/monty/test_cases/if_else_expr__all.py +0 -55
- package/ref-monty/crates/monty/test_cases/import__error_cannot_import.py +0 -9
- package/ref-monty/crates/monty/test_cases/import__error_module_not_found.py +0 -9
- package/ref-monty/crates/monty/test_cases/import__local_scope.py +0 -68
- package/ref-monty/crates/monty/test_cases/import__os.py +0 -25
- package/ref-monty/crates/monty/test_cases/import__relative_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/import__relative_no_module_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/import__runtime_error_when_executed.py +0 -14
- package/ref-monty/crates/monty/test_cases/import__star_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/import__sys.py +0 -47
- package/ref-monty/crates/monty/test_cases/import__sys_monty.py +0 -28
- package/ref-monty/crates/monty/test_cases/import__type_checking_guard.py +0 -37
- package/ref-monty/crates/monty/test_cases/import__typing.py +0 -25
- package/ref-monty/crates/monty/test_cases/import__typing_type_ignore.py +0 -4
- package/ref-monty/crates/monty/test_cases/int__bigint.py +0 -467
- package/ref-monty/crates/monty/test_cases/int__bigint_errors.py +0 -260
- package/ref-monty/crates/monty/test_cases/int__ops.py +0 -219
- package/ref-monty/crates/monty/test_cases/int__overflow_division.py +0 -84
- package/ref-monty/crates/monty/test_cases/is_variant__all.py +0 -36
- package/ref-monty/crates/monty/test_cases/isinstance__arg2_list_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/isinstance__arg2_type_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/iter__dict_mutation.py +0 -4
- package/ref-monty/crates/monty/test_cases/iter__for.py +0 -243
- package/ref-monty/crates/monty/test_cases/iter__for_loop_unpacking.py +0 -66
- package/ref-monty/crates/monty/test_cases/iter__generator_expr.py +0 -20
- package/ref-monty/crates/monty/test_cases/iter__generator_expr_type.py +0 -7
- package/ref-monty/crates/monty/test_cases/iter__not_iterable.py +0 -3
- package/ref-monty/crates/monty/test_cases/lambda__all.py +0 -145
- package/ref-monty/crates/monty/test_cases/list__extend_not_iterable.py +0 -7
- package/ref-monty/crates/monty/test_cases/list__getitem_out_of_bounds.py +0 -3
- package/ref-monty/crates/monty/test_cases/list__index_not_found.py +0 -9
- package/ref-monty/crates/monty/test_cases/list__index_start_gt_end.py +0 -10
- package/ref-monty/crates/monty/test_cases/list__ops.py +0 -473
- package/ref-monty/crates/monty/test_cases/list__pop_empty.py +0 -9
- package/ref-monty/crates/monty/test_cases/list__pop_out_of_range.py +0 -9
- package/ref-monty/crates/monty/test_cases/list__pop_type_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/list__remove_not_found.py +0 -9
- package/ref-monty/crates/monty/test_cases/list__setitem_dict_index.py +0 -13
- package/ref-monty/crates/monty/test_cases/list__setitem_huge_int_index.py +0 -13
- package/ref-monty/crates/monty/test_cases/list__setitem_index_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/list__setitem_type_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/list__unpack_type_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/longint__index_error.py +0 -3
- package/ref-monty/crates/monty/test_cases/longint__repeat_error.py +0 -3
- package/ref-monty/crates/monty/test_cases/loop__break_continue.py +0 -113
- package/ref-monty/crates/monty/test_cases/loop__break_finally.py +0 -69
- package/ref-monty/crates/monty/test_cases/loop__break_in_function_error.py +0 -13
- package/ref-monty/crates/monty/test_cases/loop__break_in_if_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/loop__break_nested_except_clears.py +0 -55
- package/ref-monty/crates/monty/test_cases/loop__break_outside_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/loop__continue_finally.py +0 -81
- package/ref-monty/crates/monty/test_cases/loop__continue_in_function_error.py +0 -13
- package/ref-monty/crates/monty/test_cases/loop__continue_in_if_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/loop__continue_nested_except_clears.py +0 -60
- package/ref-monty/crates/monty/test_cases/loop__continue_outside_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/math__acos_domain_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__acosh_domain_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__asin_domain_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__atanh_domain_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__cos_inf_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__cosh_overflow_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__exp_overflow_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__factorial_float_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__factorial_negative_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__floor_inf_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__floor_nan_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__floor_str_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__fmod_inf_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__gamma_neg_int_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__gcd_float_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__isqrt_negative_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__ldexp_overflow_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__log1p_domain_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__log_base1_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__log_zero_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__module.py +0 -1432
- package/ref-monty/crates/monty/test_cases/math__pow_domain_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__sin_inf_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__sqrt_negative_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__tan_inf_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/math__trunc_str_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/method__args_kwargs_unpacking.py +0 -259
- package/ref-monty/crates/monty/test_cases/name_error__unbound_local_func.py +0 -19
- package/ref-monty/crates/monty/test_cases/name_error__unbound_local_module.py +0 -12
- package/ref-monty/crates/monty/test_cases/name_error__undefined_call_chained.py +0 -9
- package/ref-monty/crates/monty/test_cases/name_error__undefined_call_in_expr.py +0 -9
- package/ref-monty/crates/monty/test_cases/name_error__undefined_call_in_function.py +0 -16
- package/ref-monty/crates/monty/test_cases/name_error__undefined_call_with_args.py +0 -9
- package/ref-monty/crates/monty/test_cases/name_error__undefined_global.py +0 -10
- package/ref-monty/crates/monty/test_cases/namedtuple__missing_attr.py +0 -11
- package/ref-monty/crates/monty/test_cases/namedtuple__ops.py +0 -34
- package/ref-monty/crates/monty/test_cases/nonlocal__error_module_level.py +0 -3
- package/ref-monty/crates/monty/test_cases/nonlocal__ops.py +0 -353
- package/ref-monty/crates/monty/test_cases/os__environ.py +0 -40
- package/ref-monty/crates/monty/test_cases/os__getenv_key_list_error.py +0 -5
- package/ref-monty/crates/monty/test_cases/os__getenv_key_type_error.py +0 -5
- package/ref-monty/crates/monty/test_cases/parse_error__complex.py +0 -3
- package/ref-monty/crates/monty/test_cases/pathlib__import.py +0 -11
- package/ref-monty/crates/monty/test_cases/pathlib__os.py +0 -136
- package/ref-monty/crates/monty/test_cases/pathlib__os_read_error.py +0 -12
- package/ref-monty/crates/monty/test_cases/pathlib__pure.py +0 -81
- package/ref-monty/crates/monty/test_cases/pyobject__cycle_dict_self.py +0 -5
- package/ref-monty/crates/monty/test_cases/pyobject__cycle_list_dict.py +0 -6
- package/ref-monty/crates/monty/test_cases/pyobject__cycle_list_self.py +0 -5
- package/ref-monty/crates/monty/test_cases/pyobject__cycle_multiple_refs.py +0 -6
- package/ref-monty/crates/monty/test_cases/range__error_no_args.py +0 -2
- package/ref-monty/crates/monty/test_cases/range__error_step_zero.py +0 -2
- package/ref-monty/crates/monty/test_cases/range__error_too_many_args.py +0 -2
- package/ref-monty/crates/monty/test_cases/range__getitem_index_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/range__ops.py +0 -236
- package/ref-monty/crates/monty/test_cases/re__basic.py +0 -756
- package/ref-monty/crates/monty/test_cases/re__grouping.py +0 -241
- package/ref-monty/crates/monty/test_cases/re__match.py +0 -148
- package/ref-monty/crates/monty/test_cases/recursion__deep_drop.py +0 -26
- package/ref-monty/crates/monty/test_cases/recursion__deep_eq.py +0 -23
- package/ref-monty/crates/monty/test_cases/recursion__deep_hash.py +0 -46
- package/ref-monty/crates/monty/test_cases/recursion__deep_repr.py +0 -12
- package/ref-monty/crates/monty/test_cases/recursion__function_depth.py +0 -13
- package/ref-monty/crates/monty/test_cases/refcount__cycle_mutual_reference.py +0 -18
- package/ref-monty/crates/monty/test_cases/refcount__cycle_self_reference.py +0 -12
- package/ref-monty/crates/monty/test_cases/refcount__dict_basic.py +0 -5
- package/ref-monty/crates/monty/test_cases/refcount__dict_get.py +0 -5
- package/ref-monty/crates/monty/test_cases/refcount__dict_keys_and.py +0 -14
- package/ref-monty/crates/monty/test_cases/refcount__dict_overwrite.py +0 -6
- package/ref-monty/crates/monty/test_cases/refcount__gather_cleanup.py +0 -16
- package/ref-monty/crates/monty/test_cases/refcount__gather_exception.py +0 -18
- package/ref-monty/crates/monty/test_cases/refcount__gather_nested_cancel.py +0 -25
- package/ref-monty/crates/monty/test_cases/refcount__immediate_skipped.py +0 -4
- package/ref-monty/crates/monty/test_cases/refcount__kwargs_unpacking.py +0 -27
- package/ref-monty/crates/monty/test_cases/refcount__list_append_multiple.py +0 -6
- package/ref-monty/crates/monty/test_cases/refcount__list_append_ref.py +0 -5
- package/ref-monty/crates/monty/test_cases/refcount__list_concat.py +0 -5
- package/ref-monty/crates/monty/test_cases/refcount__list_getitem.py +0 -5
- package/ref-monty/crates/monty/test_cases/refcount__list_iadd.py +0 -5
- package/ref-monty/crates/monty/test_cases/refcount__nested_list.py +0 -4
- package/ref-monty/crates/monty/test_cases/refcount__re_pattern_sub_error_paths.py +0 -37
- package/ref-monty/crates/monty/test_cases/refcount__re_search_match.py +0 -34
- package/ref-monty/crates/monty/test_cases/refcount__re_sub_error_paths.py +0 -31
- package/ref-monty/crates/monty/test_cases/refcount__shared_reference.py +0 -4
- package/ref-monty/crates/monty/test_cases/refcount__single_list.py +0 -3
- package/ref-monty/crates/monty/test_cases/repr__cycle_detection.py +0 -24
- package/ref-monty/crates/monty/test_cases/set__ops.py +0 -191
- package/ref-monty/crates/monty/test_cases/set__review_bugs.py +0 -35
- package/ref-monty/crates/monty/test_cases/set__unpack_type_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/slice__invalid_indices.py +0 -2
- package/ref-monty/crates/monty/test_cases/slice__kwargs.py +0 -9
- package/ref-monty/crates/monty/test_cases/slice__no_args.py +0 -9
- package/ref-monty/crates/monty/test_cases/slice__ops.py +0 -149
- package/ref-monty/crates/monty/test_cases/slice__step_zero.py +0 -9
- package/ref-monty/crates/monty/test_cases/slice__step_zero_bytes.py +0 -9
- package/ref-monty/crates/monty/test_cases/slice__step_zero_range.py +0 -9
- package/ref-monty/crates/monty/test_cases/slice__step_zero_str.py +0 -9
- package/ref-monty/crates/monty/test_cases/slice__step_zero_tuple.py +0 -9
- package/ref-monty/crates/monty/test_cases/slice__too_many_args.py +0 -9
- package/ref-monty/crates/monty/test_cases/str__getitem_index_error.py +0 -10
- package/ref-monty/crates/monty/test_cases/str__index_not_found.py +0 -9
- package/ref-monty/crates/monty/test_cases/str__join_no_args.py +0 -9
- package/ref-monty/crates/monty/test_cases/str__join_non_string.py +0 -9
- package/ref-monty/crates/monty/test_cases/str__join_not_iterable.py +0 -9
- package/ref-monty/crates/monty/test_cases/str__join_too_many_args.py +0 -9
- package/ref-monty/crates/monty/test_cases/str__methods.py +0 -327
- package/ref-monty/crates/monty/test_cases/str__ops.py +0 -162
- package/ref-monty/crates/monty/test_cases/str__partition_empty.py +0 -9
- package/ref-monty/crates/monty/test_cases/str__rsplit_empty_sep.py +0 -9
- package/ref-monty/crates/monty/test_cases/str__split_empty_sep.py +0 -9
- package/ref-monty/crates/monty/test_cases/sys__types.py +0 -7
- package/ref-monty/crates/monty/test_cases/traceback__division_error.py +0 -30
- package/ref-monty/crates/monty/test_cases/traceback__index_error.py +0 -17
- package/ref-monty/crates/monty/test_cases/traceback__insert_as_int.py +0 -10
- package/ref-monty/crates/monty/test_cases/traceback__nested_call.py +0 -29
- package/ref-monty/crates/monty/test_cases/traceback__nonlocal_module_scope.py +0 -10
- package/ref-monty/crates/monty/test_cases/traceback__nonlocal_unbound.py +0 -24
- package/ref-monty/crates/monty/test_cases/traceback__range_as_int.py +0 -9
- package/ref-monty/crates/monty/test_cases/traceback__recursion_error.py +0 -23
- package/ref-monty/crates/monty/test_cases/traceback__set_mutation.py +0 -11
- package/ref-monty/crates/monty/test_cases/traceback__undefined_attr_call.py +0 -16
- package/ref-monty/crates/monty/test_cases/traceback__undefined_call.py +0 -16
- package/ref-monty/crates/monty/test_cases/traceback__undefined_raise.py +0 -16
- package/ref-monty/crates/monty/test_cases/try_except__all.py +0 -472
- package/ref-monty/crates/monty/test_cases/try_except__bare_raise_no_context.py +0 -2
- package/ref-monty/crates/monty/test_cases/try_except__invalid_type.py +0 -5
- package/ref-monty/crates/monty/test_cases/tuple__getitem_out_of_bounds.py +0 -3
- package/ref-monty/crates/monty/test_cases/tuple__index_not_found.py +0 -9
- package/ref-monty/crates/monty/test_cases/tuple__index_start_gt_end.py +0 -10
- package/ref-monty/crates/monty/test_cases/tuple__methods.py +0 -19
- package/ref-monty/crates/monty/test_cases/tuple__ops.py +0 -133
- package/ref-monty/crates/monty/test_cases/tuple__unpack_type_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/type__builtin_attr_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/type__bytes_negative.py +0 -2
- package/ref-monty/crates/monty/test_cases/type__cell_not_builtin.py +0 -9
- package/ref-monty/crates/monty/test_cases/type__exception_attr_error.py +0 -11
- package/ref-monty/crates/monty/test_cases/type__float_conversion_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/type__float_repr_both_quotes.py +0 -9
- package/ref-monty/crates/monty/test_cases/type__float_repr_newline.py +0 -9
- package/ref-monty/crates/monty/test_cases/type__float_repr_single_quote.py +0 -9
- package/ref-monty/crates/monty/test_cases/type__int_conversion_error.py +0 -2
- package/ref-monty/crates/monty/test_cases/type__list_not_iterable.py +0 -2
- package/ref-monty/crates/monty/test_cases/type__non_builtin_name_error.py +0 -9
- package/ref-monty/crates/monty/test_cases/type__ops.py +0 -200
- package/ref-monty/crates/monty/test_cases/type__shadow_exc.py +0 -3
- package/ref-monty/crates/monty/test_cases/type__shadow_int.py +0 -9
- package/ref-monty/crates/monty/test_cases/type__shadow_len.py +0 -3
- package/ref-monty/crates/monty/test_cases/type__tuple_not_iterable.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__int_add_list.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__int_div_str.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__int_floordiv_str.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__int_iadd_str.py +0 -3
- package/ref-monty/crates/monty/test_cases/type_error__int_mod_str.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__int_pow_str.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__int_sub_str.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__list_add_int.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__list_add_str.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__list_iadd_int.py +0 -6
- package/ref-monty/crates/monty/test_cases/type_error__str_add_int.py +0 -2
- package/ref-monty/crates/monty/test_cases/type_error__str_iadd_int.py +0 -3
- package/ref-monty/crates/monty/test_cases/type_error__unary_invert_str.py +0 -3
- package/ref-monty/crates/monty/test_cases/type_error__unary_minus_str.py +0 -4
- package/ref-monty/crates/monty/test_cases/type_error__unary_neg_str.py +0 -3
- package/ref-monty/crates/monty/test_cases/type_error__unary_plus_str.py +0 -4
- package/ref-monty/crates/monty/test_cases/typing__types.py +0 -24
- package/ref-monty/crates/monty/test_cases/unpack__nested.py +0 -48
- package/ref-monty/crates/monty/test_cases/unpack__non_sequence.py +0 -9
- package/ref-monty/crates/monty/test_cases/unpack__not_enough.py +0 -9
- package/ref-monty/crates/monty/test_cases/unpack__ops.py +0 -153
- package/ref-monty/crates/monty/test_cases/unpack__star_not_enough.py +0 -9
- package/ref-monty/crates/monty/test_cases/unpack__too_many.py +0 -9
- package/ref-monty/crates/monty/test_cases/version__cpython.py +0 -4
- package/ref-monty/crates/monty/test_cases/walrus__all.py +0 -178
- package/ref-monty/crates/monty/test_cases/while__all.py +0 -206
- package/ref-monty/crates/monty/tests/asyncio.rs +0 -764
- package/ref-monty/crates/monty/tests/binary_serde.rs +0 -185
- package/ref-monty/crates/monty/tests/bytecode_limits.rs +0 -248
- package/ref-monty/crates/monty/tests/datatest_runner.rs +0 -2029
- package/ref-monty/crates/monty/tests/inputs.rs +0 -420
- package/ref-monty/crates/monty/tests/json_serde.rs +0 -250
- package/ref-monty/crates/monty/tests/main.rs +0 -71
- package/ref-monty/crates/monty/tests/math_module.rs +0 -114
- package/ref-monty/crates/monty/tests/name_lookup.rs +0 -482
- package/ref-monty/crates/monty/tests/os_tests.rs +0 -459
- package/ref-monty/crates/monty/tests/parse_errors.rs +0 -441
- package/ref-monty/crates/monty/tests/print_writer.rs +0 -238
- package/ref-monty/crates/monty/tests/py_object.rs +0 -121
- package/ref-monty/crates/monty/tests/regex.rs +0 -90
- package/ref-monty/crates/monty/tests/repl.rs +0 -344
- package/ref-monty/crates/monty/tests/resource_limits.rs +0 -1826
- package/ref-monty/crates/monty/tests/try_from.rs +0 -167
- package/ref-monty/crates/monty-cli/Cargo.toml +0 -25
- package/ref-monty/crates/monty-cli/src/main.rs +0 -541
- package/ref-monty/crates/monty-js/.cargo/config.toml +0 -2
- package/ref-monty/crates/monty-js/.prettierignore +0 -8
- package/ref-monty/crates/monty-js/Cargo.toml +0 -32
- package/ref-monty/crates/monty-js/README.md +0 -207
- package/ref-monty/crates/monty-js/__test__/async.spec.ts +0 -350
- package/ref-monty/crates/monty-js/__test__/basic.spec.ts +0 -114
- package/ref-monty/crates/monty-js/__test__/exceptions.spec.ts +0 -427
- package/ref-monty/crates/monty-js/__test__/external.spec.ts +0 -354
- package/ref-monty/crates/monty-js/__test__/inputs.spec.ts +0 -143
- package/ref-monty/crates/monty-js/__test__/limits.spec.ts +0 -162
- package/ref-monty/crates/monty-js/__test__/package.json +0 -3
- package/ref-monty/crates/monty-js/__test__/print.spec.ts +0 -229
- package/ref-monty/crates/monty-js/__test__/repl.spec.ts +0 -34
- package/ref-monty/crates/monty-js/__test__/serialize.spec.ts +0 -205
- package/ref-monty/crates/monty-js/__test__/start.spec.ts +0 -443
- package/ref-monty/crates/monty-js/__test__/type_check.spec.ts +0 -147
- package/ref-monty/crates/monty-js/__test__/types.spec.ts +0 -319
- package/ref-monty/crates/monty-js/build.rs +0 -61
- package/ref-monty/crates/monty-js/index-header.d.ts +0 -3
- package/ref-monty/crates/monty-js/package-lock.json +0 -4694
- package/ref-monty/crates/monty-js/package.json +0 -100
- package/ref-monty/crates/monty-js/scripts/smoke-test.sh +0 -69
- package/ref-monty/crates/monty-js/smoke-test/package.json +0 -17
- package/ref-monty/crates/monty-js/smoke-test/test.ts +0 -171
- package/ref-monty/crates/monty-js/smoke-test/tsconfig.json +0 -11
- package/ref-monty/crates/monty-js/src/convert.rs +0 -648
- package/ref-monty/crates/monty-js/src/exceptions.rs +0 -293
- package/ref-monty/crates/monty-js/src/lib.rs +0 -41
- package/ref-monty/crates/monty-js/src/limits.rs +0 -53
- package/ref-monty/crates/monty-js/src/monty_cls.rs +0 -1407
- package/ref-monty/crates/monty-js/tsconfig.json +0 -17
- package/ref-monty/crates/monty-js/wrapper.ts +0 -701
- package/ref-monty/crates/monty-python/Cargo.toml +0 -38
- package/ref-monty/crates/monty-python/README.md +0 -134
- package/ref-monty/crates/monty-python/build.rs +0 -4
- package/ref-monty/crates/monty-python/example.py +0 -40
- package/ref-monty/crates/monty-python/exercise.py +0 -46
- package/ref-monty/crates/monty-python/pyproject.toml +0 -57
- package/ref-monty/crates/monty-python/python/pydantic_monty/__init__.py +0 -281
- package/ref-monty/crates/monty-python/python/pydantic_monty/_monty.pyi +0 -677
- package/ref-monty/crates/monty-python/python/pydantic_monty/os_access.py +0 -933
- package/ref-monty/crates/monty-python/python/pydantic_monty/py.typed +0 -0
- package/ref-monty/crates/monty-python/src/convert.rs +0 -273
- package/ref-monty/crates/monty-python/src/dataclass.rs +0 -461
- package/ref-monty/crates/monty-python/src/exceptions.rs +0 -557
- package/ref-monty/crates/monty-python/src/external.rs +0 -165
- package/ref-monty/crates/monty-python/src/lib.rs +0 -77
- package/ref-monty/crates/monty-python/src/limits.rs +0 -142
- package/ref-monty/crates/monty-python/src/monty_cls.rs +0 -1650
- package/ref-monty/crates/monty-python/src/repl.rs +0 -470
- package/ref-monty/crates/monty-python/src/serialization.rs +0 -761
- package/ref-monty/crates/monty-python/tests/test_async.py +0 -1201
- package/ref-monty/crates/monty-python/tests/test_basic.py +0 -66
- package/ref-monty/crates/monty-python/tests/test_dataclasses.py +0 -971
- package/ref-monty/crates/monty-python/tests/test_exceptions.py +0 -361
- package/ref-monty/crates/monty-python/tests/test_external.py +0 -367
- package/ref-monty/crates/monty-python/tests/test_inputs.py +0 -126
- package/ref-monty/crates/monty-python/tests/test_limits.py +0 -257
- package/ref-monty/crates/monty-python/tests/test_os_access.py +0 -1286
- package/ref-monty/crates/monty-python/tests/test_os_access_compat.py +0 -731
- package/ref-monty/crates/monty-python/tests/test_os_access_raw.py +0 -483
- package/ref-monty/crates/monty-python/tests/test_os_calls.py +0 -819
- package/ref-monty/crates/monty-python/tests/test_print.py +0 -208
- package/ref-monty/crates/monty-python/tests/test_re.py +0 -170
- package/ref-monty/crates/monty-python/tests/test_readme_examples.py +0 -20
- package/ref-monty/crates/monty-python/tests/test_repl.py +0 -749
- package/ref-monty/crates/monty-python/tests/test_serialize.py +0 -284
- package/ref-monty/crates/monty-python/tests/test_start.py +0 -346
- package/ref-monty/crates/monty-python/tests/test_threading.py +0 -163
- package/ref-monty/crates/monty-python/tests/test_type_check.py +0 -344
- package/ref-monty/crates/monty-python/tests/test_types.py +0 -553
- package/ref-monty/crates/monty-type-checking/Cargo.toml +0 -32
- package/ref-monty/crates/monty-type-checking/src/db.rs +0 -116
- package/ref-monty/crates/monty-type-checking/src/lib.rs +0 -4
- package/ref-monty/crates/monty-type-checking/src/type_check.rs +0 -280
- package/ref-monty/crates/monty-type-checking/tests/bad_types.py +0 -109
- package/ref-monty/crates/monty-type-checking/tests/bad_types_output.txt +0 -21
- package/ref-monty/crates/monty-type-checking/tests/good_types.py +0 -475
- package/ref-monty/crates/monty-type-checking/tests/main.rs +0 -205
- package/ref-monty/crates/monty-type-checking/tests/reveal_types.py +0 -56
- package/ref-monty/crates/monty-type-checking/tests/reveal_types_output.txt +0 -41
- package/ref-monty/crates/monty-typeshed/Cargo.toml +0 -29
- package/ref-monty/crates/monty-typeshed/README.md +0 -11
- package/ref-monty/crates/monty-typeshed/build.rs +0 -101
- package/ref-monty/crates/monty-typeshed/custom/README.md +0 -1
- package/ref-monty/crates/monty-typeshed/custom/asyncio.pyi +0 -138
- package/ref-monty/crates/monty-typeshed/custom/os.pyi +0 -87
- package/ref-monty/crates/monty-typeshed/custom/sys.pyi +0 -33
- package/ref-monty/crates/monty-typeshed/src/lib.rs +0 -56
- package/ref-monty/crates/monty-typeshed/update.py +0 -321
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/source_commit.txt +0 -1
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/VERSIONS +0 -20
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/_collections_abc.pyi +0 -105
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/_typeshed/__init__.pyi +0 -394
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/asyncio.pyi +0 -138
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/builtins.pyi +0 -1434
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/collections/__init__.pyi +0 -527
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/collections/abc.pyi +0 -2
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/dataclasses.pyi +0 -502
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/enum.pyi +0 -376
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/math.pyi +0 -149
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/os.pyi +0 -87
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/pathlib/__init__.pyi +0 -395
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/pathlib/types.pyi +0 -8
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/re.pyi +0 -337
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/sys.pyi +0 -33
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/types.pyi +0 -741
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/typing.pyi +0 -1217
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/typing_extensions.pyi +0 -716
- package/ref-monty/docs/usage-guide.md +0 -117
- package/ref-monty/examples/README.md +0 -3
- package/ref-monty/examples/expense_analysis/README.md +0 -3
- package/ref-monty/examples/expense_analysis/data.py +0 -124
- package/ref-monty/examples/expense_analysis/main.py +0 -115
- package/ref-monty/examples/sql_playground/README.md +0 -20
- package/ref-monty/examples/sql_playground/external_functions.py +0 -129
- package/ref-monty/examples/sql_playground/main.py +0 -81
- package/ref-monty/examples/sql_playground/sandbox_code.py +0 -82
- package/ref-monty/examples/sql_playground/type_stubs.pyi +0 -14
- package/ref-monty/examples/web_scraper/README.md +0 -15
- package/ref-monty/examples/web_scraper/browser.py +0 -56
- package/ref-monty/examples/web_scraper/example_code.py +0 -59
- package/ref-monty/examples/web_scraper/external_functions.py +0 -324
- package/ref-monty/examples/web_scraper/main.py +0 -193
- package/ref-monty/examples/web_scraper/sub_agent.py +0 -79
- package/ref-monty/monty-npm.md +0 -235
- package/ref-monty/pyproject.toml +0 -162
- package/ref-monty/scripts/check_imports.py +0 -91
- package/ref-monty/scripts/codecov_diff.py +0 -412
- package/ref-monty/scripts/complete_tests.py +0 -146
- package/ref-monty/scripts/flamegraph_to_text.py +0 -208
- package/ref-monty/scripts/iter_test_methods.py +0 -540
- package/ref-monty/scripts/run_traceback.py +0 -180
- package/ref-monty/scripts/startup_performance.py +0 -130
- package/ref-monty/uv.lock +0 -1779
- package/temp_resend_cli/repo/.github/scripts/pr-title-check.js +0 -34
- package/temp_resend_cli/repo/.github/workflows/ci.yml +0 -67
- package/temp_resend_cli/repo/.github/workflows/post-release.yml +0 -51
- package/temp_resend_cli/repo/.github/workflows/pr-title-check.yml +0 -13
- package/temp_resend_cli/repo/.github/workflows/release.yml +0 -175
- package/temp_resend_cli/repo/.github/workflows/test-install-unix.yml +0 -34
- package/temp_resend_cli/repo/.github/workflows/test-install-windows.yml +0 -48
- package/temp_resend_cli/repo/CHANGELOG.md +0 -31
- package/temp_resend_cli/repo/LICENSE +0 -21
- package/temp_resend_cli/repo/README.md +0 -450
- package/temp_resend_cli/repo/biome.json +0 -36
- package/temp_resend_cli/repo/install.ps1 +0 -141
- package/temp_resend_cli/repo/install.sh +0 -301
- package/temp_resend_cli/repo/package.json +0 -61
- package/temp_resend_cli/repo/pnpm-lock.yaml +0 -2439
- package/temp_resend_cli/repo/renovate.json +0 -4
- package/temp_resend_cli/repo/src/cli.ts +0 -98
- package/temp_resend_cli/repo/src/commands/api-keys/create.ts +0 -114
- package/temp_resend_cli/repo/src/commands/api-keys/delete.ts +0 -47
- package/temp_resend_cli/repo/src/commands/api-keys/index.ts +0 -26
- package/temp_resend_cli/repo/src/commands/api-keys/list.ts +0 -35
- package/temp_resend_cli/repo/src/commands/api-keys/utils.ts +0 -8
- package/temp_resend_cli/repo/src/commands/auth/index.ts +0 -20
- package/temp_resend_cli/repo/src/commands/auth/login.ts +0 -234
- package/temp_resend_cli/repo/src/commands/auth/logout.ts +0 -105
- package/temp_resend_cli/repo/src/commands/broadcasts/create.ts +0 -196
- package/temp_resend_cli/repo/src/commands/broadcasts/delete.ts +0 -46
- package/temp_resend_cli/repo/src/commands/broadcasts/get.ts +0 -59
- package/temp_resend_cli/repo/src/commands/broadcasts/index.ts +0 -43
- package/temp_resend_cli/repo/src/commands/broadcasts/list.ts +0 -60
- package/temp_resend_cli/repo/src/commands/broadcasts/send.ts +0 -56
- package/temp_resend_cli/repo/src/commands/broadcasts/update.ts +0 -95
- package/temp_resend_cli/repo/src/commands/broadcasts/utils.ts +0 -35
- package/temp_resend_cli/repo/src/commands/contact-properties/create.ts +0 -118
- package/temp_resend_cli/repo/src/commands/contact-properties/delete.ts +0 -48
- package/temp_resend_cli/repo/src/commands/contact-properties/get.ts +0 -46
- package/temp_resend_cli/repo/src/commands/contact-properties/index.ts +0 -48
- package/temp_resend_cli/repo/src/commands/contact-properties/list.ts +0 -68
- package/temp_resend_cli/repo/src/commands/contact-properties/update.ts +0 -88
- package/temp_resend_cli/repo/src/commands/contact-properties/utils.ts +0 -17
- package/temp_resend_cli/repo/src/commands/contacts/add-segment.ts +0 -78
- package/temp_resend_cli/repo/src/commands/contacts/create.ts +0 -122
- package/temp_resend_cli/repo/src/commands/contacts/delete.ts +0 -49
- package/temp_resend_cli/repo/src/commands/contacts/get.ts +0 -53
- package/temp_resend_cli/repo/src/commands/contacts/index.ts +0 -58
- package/temp_resend_cli/repo/src/commands/contacts/list.ts +0 -57
- package/temp_resend_cli/repo/src/commands/contacts/remove-segment.ts +0 -48
- package/temp_resend_cli/repo/src/commands/contacts/segments.ts +0 -39
- package/temp_resend_cli/repo/src/commands/contacts/topics.ts +0 -45
- package/temp_resend_cli/repo/src/commands/contacts/update-topics.ts +0 -90
- package/temp_resend_cli/repo/src/commands/contacts/update.ts +0 -77
- package/temp_resend_cli/repo/src/commands/contacts/utils.ts +0 -119
- package/temp_resend_cli/repo/src/commands/doctor.ts +0 -216
- package/temp_resend_cli/repo/src/commands/domains/create.ts +0 -83
- package/temp_resend_cli/repo/src/commands/domains/delete.ts +0 -42
- package/temp_resend_cli/repo/src/commands/domains/get.ts +0 -47
- package/temp_resend_cli/repo/src/commands/domains/index.ts +0 -35
- package/temp_resend_cli/repo/src/commands/domains/list.ts +0 -53
- package/temp_resend_cli/repo/src/commands/domains/update.ts +0 -75
- package/temp_resend_cli/repo/src/commands/domains/utils.ts +0 -44
- package/temp_resend_cli/repo/src/commands/domains/verify.ts +0 -38
- package/temp_resend_cli/repo/src/commands/emails/batch.ts +0 -140
- package/temp_resend_cli/repo/src/commands/emails/get.ts +0 -44
- package/temp_resend_cli/repo/src/commands/emails/index.ts +0 -30
- package/temp_resend_cli/repo/src/commands/emails/list.ts +0 -84
- package/temp_resend_cli/repo/src/commands/emails/receiving/attachment.ts +0 -55
- package/temp_resend_cli/repo/src/commands/emails/receiving/attachments.ts +0 -68
- package/temp_resend_cli/repo/src/commands/emails/receiving/get.ts +0 -58
- package/temp_resend_cli/repo/src/commands/emails/receiving/index.ts +0 -28
- package/temp_resend_cli/repo/src/commands/emails/receiving/list.ts +0 -59
- package/temp_resend_cli/repo/src/commands/emails/receiving/utils.ts +0 -38
- package/temp_resend_cli/repo/src/commands/emails/send.ts +0 -189
- package/temp_resend_cli/repo/src/commands/open.ts +0 -27
- package/temp_resend_cli/repo/src/commands/segments/create.ts +0 -50
- package/temp_resend_cli/repo/src/commands/segments/delete.ts +0 -47
- package/temp_resend_cli/repo/src/commands/segments/get.ts +0 -38
- package/temp_resend_cli/repo/src/commands/segments/index.ts +0 -36
- package/temp_resend_cli/repo/src/commands/segments/list.ts +0 -58
- package/temp_resend_cli/repo/src/commands/segments/utils.ts +0 -7
- package/temp_resend_cli/repo/src/commands/teams/index.ts +0 -10
- package/temp_resend_cli/repo/src/commands/teams/list.ts +0 -35
- package/temp_resend_cli/repo/src/commands/teams/remove.ts +0 -86
- package/temp_resend_cli/repo/src/commands/teams/switch.ts +0 -76
- package/temp_resend_cli/repo/src/commands/topics/create.ts +0 -73
- package/temp_resend_cli/repo/src/commands/topics/delete.ts +0 -47
- package/temp_resend_cli/repo/src/commands/topics/get.ts +0 -42
- package/temp_resend_cli/repo/src/commands/topics/index.ts +0 -42
- package/temp_resend_cli/repo/src/commands/topics/list.ts +0 -34
- package/temp_resend_cli/repo/src/commands/topics/update.ts +0 -59
- package/temp_resend_cli/repo/src/commands/topics/utils.ts +0 -16
- package/temp_resend_cli/repo/src/commands/webhooks/create.ts +0 -128
- package/temp_resend_cli/repo/src/commands/webhooks/delete.ts +0 -49
- package/temp_resend_cli/repo/src/commands/webhooks/get.ts +0 -42
- package/temp_resend_cli/repo/src/commands/webhooks/index.ts +0 -42
- package/temp_resend_cli/repo/src/commands/webhooks/list.ts +0 -55
- package/temp_resend_cli/repo/src/commands/webhooks/listen.ts +0 -379
- package/temp_resend_cli/repo/src/commands/webhooks/update.ts +0 -83
- package/temp_resend_cli/repo/src/commands/webhooks/utils.ts +0 -36
- package/temp_resend_cli/repo/src/commands/whoami.ts +0 -71
- package/temp_resend_cli/repo/src/lib/actions.ts +0 -157
- package/temp_resend_cli/repo/src/lib/client.ts +0 -37
- package/temp_resend_cli/repo/src/lib/config.ts +0 -217
- package/temp_resend_cli/repo/src/lib/files.ts +0 -15
- package/temp_resend_cli/repo/src/lib/help-text.ts +0 -38
- package/temp_resend_cli/repo/src/lib/output.ts +0 -56
- package/temp_resend_cli/repo/src/lib/pagination.ts +0 -36
- package/temp_resend_cli/repo/src/lib/prompts.ts +0 -149
- package/temp_resend_cli/repo/src/lib/spinner.ts +0 -100
- package/temp_resend_cli/repo/src/lib/table.ts +0 -57
- package/temp_resend_cli/repo/src/lib/tty.ts +0 -28
- package/temp_resend_cli/repo/src/lib/update-check.ts +0 -169
- package/temp_resend_cli/repo/src/lib/version.ts +0 -4
- package/temp_resend_cli/repo/tests/commands/api-keys/create.test.ts +0 -196
- package/temp_resend_cli/repo/tests/commands/api-keys/delete.test.ts +0 -157
- package/temp_resend_cli/repo/tests/commands/api-keys/list.test.ts +0 -134
- package/temp_resend_cli/repo/tests/commands/auth/login.test.ts +0 -153
- package/temp_resend_cli/repo/tests/commands/auth/logout.test.ts +0 -153
- package/temp_resend_cli/repo/tests/commands/broadcasts/create.test.ts +0 -454
- package/temp_resend_cli/repo/tests/commands/broadcasts/delete.test.ts +0 -183
- package/temp_resend_cli/repo/tests/commands/broadcasts/get.test.ts +0 -147
- package/temp_resend_cli/repo/tests/commands/broadcasts/list.test.ts +0 -199
- package/temp_resend_cli/repo/tests/commands/broadcasts/send.test.ts +0 -162
- package/temp_resend_cli/repo/tests/commands/broadcasts/update.test.ts +0 -288
- package/temp_resend_cli/repo/tests/commands/contact-properties/create.test.ts +0 -251
- package/temp_resend_cli/repo/tests/commands/contact-properties/delete.test.ts +0 -184
- package/temp_resend_cli/repo/tests/commands/contact-properties/get.test.ts +0 -145
- package/temp_resend_cli/repo/tests/commands/contact-properties/list.test.ts +0 -181
- package/temp_resend_cli/repo/tests/commands/contact-properties/update.test.ts +0 -217
- package/temp_resend_cli/repo/tests/commands/contacts/add-segment.test.ts +0 -189
- package/temp_resend_cli/repo/tests/commands/contacts/create.test.ts +0 -271
- package/temp_resend_cli/repo/tests/commands/contacts/delete.test.ts +0 -193
- package/temp_resend_cli/repo/tests/commands/contacts/get.test.ts +0 -149
- package/temp_resend_cli/repo/tests/commands/contacts/list.test.ts +0 -176
- package/temp_resend_cli/repo/tests/commands/contacts/remove-segment.test.ts +0 -167
- package/temp_resend_cli/repo/tests/commands/contacts/segments.test.ts +0 -168
- package/temp_resend_cli/repo/tests/commands/contacts/topics.test.ts +0 -164
- package/temp_resend_cli/repo/tests/commands/contacts/update-topics.test.ts +0 -248
- package/temp_resend_cli/repo/tests/commands/contacts/update.test.ts +0 -206
- package/temp_resend_cli/repo/tests/commands/doctor.test.ts +0 -164
- package/temp_resend_cli/repo/tests/commands/domains/create.test.ts +0 -193
- package/temp_resend_cli/repo/tests/commands/domains/delete.test.ts +0 -157
- package/temp_resend_cli/repo/tests/commands/domains/get.test.ts +0 -138
- package/temp_resend_cli/repo/tests/commands/domains/list.test.ts +0 -165
- package/temp_resend_cli/repo/tests/commands/domains/update.test.ts +0 -224
- package/temp_resend_cli/repo/tests/commands/domains/verify.test.ts +0 -118
- package/temp_resend_cli/repo/tests/commands/emails/batch.test.ts +0 -324
- package/temp_resend_cli/repo/tests/commands/emails/get.test.ts +0 -132
- package/temp_resend_cli/repo/tests/commands/emails/receiving/attachment.test.ts +0 -141
- package/temp_resend_cli/repo/tests/commands/emails/receiving/attachments.test.ts +0 -169
- package/temp_resend_cli/repo/tests/commands/emails/receiving/get.test.ts +0 -141
- package/temp_resend_cli/repo/tests/commands/emails/receiving/list.test.ts +0 -182
- package/temp_resend_cli/repo/tests/commands/emails/send.test.ts +0 -312
- package/temp_resend_cli/repo/tests/commands/segments/create.test.ts +0 -164
- package/temp_resend_cli/repo/tests/commands/segments/delete.test.ts +0 -183
- package/temp_resend_cli/repo/tests/commands/segments/get.test.ts +0 -138
- package/temp_resend_cli/repo/tests/commands/segments/list.test.ts +0 -174
- package/temp_resend_cli/repo/tests/commands/teams/list.test.ts +0 -62
- package/temp_resend_cli/repo/tests/commands/teams/remove.test.ts +0 -110
- package/temp_resend_cli/repo/tests/commands/teams/switch.test.ts +0 -103
- package/temp_resend_cli/repo/tests/commands/topics/create.test.ts +0 -192
- package/temp_resend_cli/repo/tests/commands/topics/delete.test.ts +0 -157
- package/temp_resend_cli/repo/tests/commands/topics/get.test.ts +0 -126
- package/temp_resend_cli/repo/tests/commands/topics/list.test.ts +0 -125
- package/temp_resend_cli/repo/tests/commands/topics/update.test.ts +0 -178
- package/temp_resend_cli/repo/tests/commands/webhooks/create.test.ts +0 -225
- package/temp_resend_cli/repo/tests/commands/webhooks/delete.test.ts +0 -157
- package/temp_resend_cli/repo/tests/commands/webhooks/get.test.ts +0 -126
- package/temp_resend_cli/repo/tests/commands/webhooks/list.test.ts +0 -178
- package/temp_resend_cli/repo/tests/commands/webhooks/update.test.ts +0 -207
- package/temp_resend_cli/repo/tests/commands/whoami.test.ts +0 -98
- package/temp_resend_cli/repo/tests/e2e/smoke.test.ts +0 -93
- package/temp_resend_cli/repo/tests/helpers.ts +0 -86
- package/temp_resend_cli/repo/tests/lib/client.test.ts +0 -71
- package/temp_resend_cli/repo/tests/lib/config.test.ts +0 -451
- package/temp_resend_cli/repo/tests/lib/files.test.ts +0 -73
- package/temp_resend_cli/repo/tests/lib/help-text.test.ts +0 -97
- package/temp_resend_cli/repo/tests/lib/output.test.ts +0 -136
- package/temp_resend_cli/repo/tests/lib/prompts.test.ts +0 -185
- package/temp_resend_cli/repo/tests/lib/spinner.test.ts +0 -166
- package/temp_resend_cli/repo/tests/lib/table.test.ts +0 -63
- package/temp_resend_cli/repo/tests/lib/tty.test.ts +0 -89
- package/temp_resend_cli/repo/tests/lib/update-check.test.ts +0 -179
- package/temp_resend_cli/repo/tsconfig.json +0 -14
- package/temp_resend_cli/repo/vitest.config.e2e.ts +0 -8
- package/temp_resend_cli/repo/vitest.config.ts +0 -10
- /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
|
-
}
|