redscript-mc 2.6.2 → 3.0.0
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/.github/workflows/ci.yml +11 -0
- package/CHANGELOG.md +18 -9
- package/README-benchmarks.md +48 -0
- package/README-vscode-test.md +251 -0
- package/RELEASE_NOTES.md +74 -0
- package/ROADMAP.md +131 -167
- package/benchmarks/_shared.ts +468 -0
- package/benchmarks/baseline.json +2816 -0
- package/benchmarks/baseline.md +13 -0
- package/benchmarks/compiler-perf.report.json +207 -0
- package/benchmarks/compiler-perf.ts +76 -0
- package/benchmarks/results.md +13 -0
- package/benchmarks/stdlib-complexity.report.json +2606 -0
- package/benchmarks/stdlib-complexity.ts +54 -0
- package/benchmarks/stdlib-size.md +57 -0
- package/benchmarks/stdlib-size.ts +91 -0
- package/coverage-report.md +177 -0
- package/dist/src/__tests__/budget.test.js +4 -0
- package/dist/src/__tests__/cache/cache-behavior.test.d.ts +10 -0
- package/dist/src/__tests__/cache/cache-behavior.test.js +425 -0
- package/dist/src/__tests__/cache-extra.test.d.ts +5 -0
- package/dist/src/__tests__/cache-extra.test.js +211 -0
- package/dist/src/__tests__/cli-init.test.d.ts +1 -0
- package/dist/src/__tests__/cli-init.test.js +97 -0
- package/dist/src/__tests__/cli-publish.test.d.ts +9 -0
- package/dist/src/__tests__/cli-publish.test.js +189 -0
- package/dist/src/__tests__/cli.test.js +76 -0
- package/dist/src/__tests__/compile-preprocess.test.d.ts +11 -0
- package/dist/src/__tests__/compile-preprocess.test.js +328 -0
- package/dist/src/__tests__/compiler/break-stmt.test.d.ts +1 -0
- package/dist/src/__tests__/compiler/break-stmt.test.js +58 -0
- package/dist/src/__tests__/compiler/const-decl.test.d.ts +1 -0
- package/dist/src/__tests__/compiler/const-decl.test.js +123 -0
- package/dist/src/__tests__/compiler/continue-stmt.test.d.ts +1 -0
- package/dist/src/__tests__/compiler/continue-stmt.test.js +67 -0
- package/dist/src/__tests__/compiler/coroutine-extended.test.d.ts +17 -0
- package/dist/src/__tests__/compiler/coroutine-extended.test.js +565 -0
- package/dist/src/__tests__/compiler/deprecated.test.d.ts +4 -0
- package/dist/src/__tests__/compiler/deprecated.test.js +285 -0
- package/dist/src/__tests__/compiler/do-while.test.d.ts +1 -0
- package/dist/src/__tests__/compiler/do-while.test.js +120 -0
- package/dist/src/__tests__/compiler/enum-payload.test.d.ts +9 -0
- package/dist/src/__tests__/compiler/enum-payload.test.js +272 -0
- package/dist/src/__tests__/compiler/interface.test.d.ts +10 -0
- package/dist/src/__tests__/compiler/interface.test.js +258 -0
- package/dist/src/__tests__/compiler/labeled-loops.test.d.ts +1 -0
- package/dist/src/__tests__/compiler/labeled-loops.test.js +263 -0
- package/dist/src/__tests__/compiler/match-string.test.d.ts +1 -0
- package/dist/src/__tests__/compiler/match-string.test.js +43 -0
- package/dist/src/__tests__/compiler/memoize.test.d.ts +1 -0
- package/dist/src/__tests__/compiler/memoize.test.js +113 -0
- package/dist/src/__tests__/compiler/method-chain.test.d.ts +5 -0
- package/dist/src/__tests__/compiler/method-chain.test.js +115 -0
- package/dist/src/__tests__/compiler/module-import.test.d.ts +12 -0
- package/dist/src/__tests__/compiler/module-import.test.js +261 -0
- package/dist/src/__tests__/compiler/option-extensions.test.d.ts +6 -0
- package/dist/src/__tests__/compiler/option-extensions.test.js +191 -0
- package/dist/src/__tests__/compiler/profile-decorator.test.d.ts +1 -0
- package/dist/src/__tests__/compiler/profile-decorator.test.js +69 -0
- package/dist/src/__tests__/compiler/string-advanced.test.d.ts +7 -0
- package/dist/src/__tests__/compiler/string-advanced.test.js +281 -0
- package/dist/src/__tests__/compiler/struct-extends.test.d.ts +1 -0
- package/dist/src/__tests__/compiler/struct-extends.test.js +95 -0
- package/dist/src/__tests__/compiler/throttle-retry.test.d.ts +1 -0
- package/dist/src/__tests__/compiler/throttle-retry.test.js +166 -0
- package/dist/src/__tests__/compiler/tuple-type.test.d.ts +10 -0
- package/dist/src/__tests__/compiler/tuple-type.test.js +229 -0
- package/dist/src/__tests__/compiler/watch-decorator.test.d.ts +1 -0
- package/dist/src/__tests__/compiler/watch-decorator.test.js +65 -0
- package/dist/src/__tests__/config/project-config.test.d.ts +1 -0
- package/dist/src/__tests__/config/project-config.test.js +199 -0
- package/dist/src/__tests__/config-decorator.test.d.ts +8 -0
- package/dist/src/__tests__/config-decorator.test.js +142 -0
- package/dist/src/__tests__/diagnostics-extra.test.d.ts +6 -0
- package/dist/src/__tests__/diagnostics-extra.test.js +132 -0
- package/dist/src/__tests__/emit/compile-branches.test.d.ts +1 -0
- package/dist/src/__tests__/emit/compile-branches.test.js +123 -0
- package/dist/src/__tests__/emit/compile-coverage.test.d.ts +25 -0
- package/dist/src/__tests__/emit/compile-coverage.test.js +617 -0
- package/dist/src/__tests__/emit/compile-extra-branches.test.d.ts +12 -0
- package/dist/src/__tests__/emit/compile-extra-branches.test.js +225 -0
- package/dist/src/__tests__/emit/compile-mocked-branches.test.d.ts +0 -0
- package/dist/src/__tests__/emit/compile-mocked-branches.test.js +238 -0
- package/dist/src/__tests__/emit/execute-chain.test.d.ts +10 -0
- package/dist/src/__tests__/emit/execute-chain.test.js +94 -0
- package/dist/src/__tests__/emit/index.test.js +2 -1
- package/dist/src/__tests__/emit/modules-branches.test.d.ts +1 -0
- package/dist/src/__tests__/emit/modules-branches.test.js +88 -0
- package/dist/src/__tests__/emit/modules-coverage.test.d.ts +15 -0
- package/dist/src/__tests__/emit/modules-coverage.test.js +221 -0
- package/dist/src/__tests__/emit/modules-errors.test.d.ts +12 -0
- package/dist/src/__tests__/emit/modules-errors.test.js +169 -0
- package/dist/src/__tests__/emit/modules-rewrite.test.d.ts +17 -0
- package/dist/src/__tests__/emit/modules-rewrite.test.js +204 -0
- package/dist/src/__tests__/emit/source-map.test.d.ts +1 -0
- package/dist/src/__tests__/emit/source-map.test.js +167 -0
- package/dist/src/__tests__/enum.test.js +9 -4
- package/dist/src/__tests__/error-recovery.test.d.ts +7 -0
- package/dist/src/__tests__/error-recovery.test.js +217 -0
- package/dist/src/__tests__/events-types-extra.test.d.ts +10 -0
- package/dist/src/__tests__/events-types-extra.test.js +91 -0
- package/dist/src/__tests__/events-types.test.d.ts +4 -0
- package/dist/src/__tests__/events-types.test.js +56 -0
- package/dist/src/__tests__/formatter.test.js +13 -5
- package/dist/src/__tests__/hir/lower-extra.test.d.ts +9 -0
- package/dist/src/__tests__/hir/lower-extra.test.js +140 -0
- package/dist/src/__tests__/hir/monomorphize-extra.test.d.ts +15 -0
- package/dist/src/__tests__/hir/monomorphize-extra.test.js +200 -0
- package/dist/src/__tests__/hir/monomorphize-extra2.test.d.ts +16 -0
- package/dist/src/__tests__/hir/monomorphize-extra2.test.js +316 -0
- package/dist/src/__tests__/incremental.test.js +10 -2
- package/dist/src/__tests__/index-extra.test.d.ts +10 -0
- package/dist/src/__tests__/index-extra.test.js +71 -0
- package/dist/src/__tests__/lexer.test.js +2 -2
- package/dist/src/__tests__/lint/rules.test.d.ts +5 -0
- package/dist/src/__tests__/lint/rules.test.js +208 -0
- package/dist/src/__tests__/lir/lower.test.js +29 -0
- package/dist/src/__tests__/lir/verify.test.js +30 -0
- package/dist/src/__tests__/lsp/completion.test.d.ts +7 -0
- package/dist/src/__tests__/lsp/completion.test.js +583 -0
- package/dist/src/__tests__/lsp/definition.test.d.ts +7 -0
- package/dist/src/__tests__/lsp/definition.test.js +454 -0
- package/dist/src/__tests__/lsp/diagnostics.test.d.ts +10 -0
- package/dist/src/__tests__/lsp/diagnostics.test.js +98 -0
- package/dist/src/__tests__/lsp/hover-docs.test.d.ts +10 -0
- package/dist/src/__tests__/lsp/hover-docs.test.js +210 -0
- package/dist/src/__tests__/lsp.test.js +4 -1
- package/dist/src/__tests__/mc-integration/item-entity-events.test.js +4 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage-2.test.js +4 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage-3.test.d.ts +13 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage-3.test.js +1227 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage-4.test.d.ts +13 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage-4.test.js +1509 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage-5.test.d.ts +14 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage-5.test.js +1374 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage-6.test.d.ts +10 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage-6.test.js +759 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage-7.test.d.ts +13 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage-7.test.js +855 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage.test.js +4 -0
- package/dist/src/__tests__/mc-integration/syntax-coverage.test.js +4 -0
- package/dist/src/__tests__/mc-validator-coverage.test.d.ts +13 -0
- package/dist/src/__tests__/mc-validator-coverage.test.js +296 -0
- package/dist/src/__tests__/mc-validator-extra.test.d.ts +13 -0
- package/dist/src/__tests__/mc-validator-extra.test.js +245 -0
- package/dist/src/__tests__/mir/lower-extra.test.d.ts +20 -0
- package/dist/src/__tests__/mir/lower-extra.test.js +361 -0
- package/dist/src/__tests__/mir/lower-extra2.test.d.ts +17 -0
- package/dist/src/__tests__/mir/lower-extra2.test.js +317 -0
- package/dist/src/__tests__/mir/lower-extra3.test.d.ts +19 -0
- package/dist/src/__tests__/mir/lower-extra3.test.js +249 -0
- package/dist/src/__tests__/mir/lower-extra4.test.d.ts +23 -0
- package/dist/src/__tests__/mir/lower-extra4.test.js +606 -0
- package/dist/src/__tests__/mir/lower-extra5.test.d.ts +25 -0
- package/dist/src/__tests__/mir/lower-extra5.test.js +543 -0
- package/dist/src/__tests__/mir/lower-extra6.test.d.ts +16 -0
- package/dist/src/__tests__/mir/lower-extra6.test.js +471 -0
- package/dist/src/__tests__/mir/lower-extra7.test.d.ts +35 -0
- package/dist/src/__tests__/mir/lower-extra7.test.js +921 -0
- package/dist/src/__tests__/mir/lower-extra8.test.d.ts +19 -0
- package/dist/src/__tests__/mir/lower-extra8.test.js +626 -0
- package/dist/src/__tests__/mir/lower-extra9.test.d.ts +14 -0
- package/dist/src/__tests__/mir/lower-extra9.test.js +717 -0
- package/dist/src/__tests__/optimizer/auto-inline.test.d.ts +1 -0
- package/dist/src/__tests__/optimizer/auto-inline.test.js +176 -0
- package/dist/src/__tests__/optimizer/cse.test.d.ts +4 -0
- package/dist/src/__tests__/optimizer/cse.test.js +178 -0
- package/dist/src/__tests__/optimizer/inline_fn.test.d.ts +1 -0
- package/dist/src/__tests__/optimizer/inline_fn.test.js +221 -0
- package/dist/src/__tests__/optimizer/licm.test.d.ts +1 -0
- package/dist/src/__tests__/optimizer/licm.test.js +244 -0
- package/dist/src/__tests__/optimizer/optimizer-extended.test.d.ts +12 -0
- package/dist/src/__tests__/optimizer/optimizer-extended.test.js +993 -0
- package/dist/src/__tests__/optimizer/strength-reduction.test.d.ts +1 -0
- package/dist/src/__tests__/optimizer/strength-reduction.test.js +86 -0
- package/dist/src/__tests__/optimizer/tco.test.d.ts +14 -0
- package/dist/src/__tests__/optimizer/tco.test.js +203 -0
- package/dist/src/__tests__/parser-coverage.test.d.ts +25 -0
- package/dist/src/__tests__/parser-coverage.test.js +491 -0
- package/dist/src/__tests__/parser-extra.test.d.ts +6 -0
- package/dist/src/__tests__/parser-extra.test.js +451 -0
- package/dist/src/__tests__/parser.test.js +12 -0
- package/dist/src/__tests__/repl-extra.test.d.ts +13 -0
- package/dist/src/__tests__/repl-extra.test.js +174 -0
- package/dist/src/__tests__/repl-server-extra.test.d.ts +10 -0
- package/dist/src/__tests__/repl-server-extra.test.js +161 -0
- package/dist/src/__tests__/repl-server.test.d.ts +6 -0
- package/dist/src/__tests__/repl-server.test.js +146 -0
- package/dist/src/__tests__/runtime-extra.test.d.ts +15 -0
- package/dist/src/__tests__/runtime-extra.test.js +732 -0
- package/dist/src/__tests__/singleton-decorator.test.d.ts +11 -0
- package/dist/src/__tests__/singleton-decorator.test.js +260 -0
- package/dist/src/__tests__/sourcemap.test.js +1 -1
- package/dist/src/__tests__/stdlib/advanced.test.d.ts +5 -0
- package/dist/src/__tests__/stdlib/advanced.test.js +301 -0
- package/dist/src/__tests__/stdlib/bigint.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/bigint.test.js +83 -0
- package/dist/src/__tests__/stdlib/bits.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/bits.test.js +96 -0
- package/dist/src/__tests__/stdlib/bossbar.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/bossbar.test.js +72 -0
- package/dist/src/__tests__/stdlib/color.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/color.test.js +84 -0
- package/dist/src/__tests__/stdlib/combat.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/combat.test.js +64 -0
- package/dist/src/__tests__/stdlib/cooldown.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/cooldown.test.js +64 -0
- package/dist/src/__tests__/stdlib/dialog.test.js +15 -7
- package/dist/src/__tests__/stdlib/ecs.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/ecs.test.js +81 -0
- package/dist/src/__tests__/stdlib/effects.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/effects.test.js +72 -0
- package/dist/src/__tests__/stdlib/events.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/events.test.js +55 -0
- package/dist/src/__tests__/stdlib/expr.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/expr.test.js +77 -0
- package/dist/src/__tests__/stdlib/fft.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/fft.test.js +82 -0
- package/dist/src/__tests__/stdlib/graph.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/graph.test.js +102 -0
- package/dist/src/__tests__/stdlib/interactions.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/interactions.test.js +60 -0
- package/dist/src/__tests__/stdlib/inventory.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/inventory.test.js +68 -0
- package/dist/src/__tests__/stdlib/linalg.test.d.ts +5 -0
- package/dist/src/__tests__/stdlib/linalg.test.js +78 -0
- package/dist/src/__tests__/stdlib/map.test.d.ts +1 -0
- package/dist/src/__tests__/stdlib/map.test.js +84 -0
- package/dist/src/__tests__/stdlib/math.test.js +19 -6
- package/dist/src/__tests__/stdlib/math_hp.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/math_hp.test.js +80 -0
- package/dist/src/__tests__/stdlib/mobs.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/mobs.test.js +61 -0
- package/dist/src/__tests__/stdlib/noise.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/noise.test.js +73 -0
- package/dist/src/__tests__/stdlib/ode.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/ode.test.js +68 -0
- package/dist/src/__tests__/stdlib/parabola.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/parabola.test.js +77 -0
- package/dist/src/__tests__/stdlib/particles.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/particles.test.js +68 -0
- package/dist/src/__tests__/stdlib/physics.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/physics.test.js +76 -0
- package/dist/src/__tests__/stdlib/player.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/player.test.js +64 -0
- package/dist/src/__tests__/stdlib/quaternion.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/quaternion.test.js +73 -0
- package/dist/src/__tests__/stdlib/queue.test.d.ts +1 -0
- package/dist/src/__tests__/stdlib/queue.test.js +97 -0
- package/dist/src/__tests__/stdlib/random.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/random.test.js +76 -0
- package/dist/src/__tests__/stdlib/result.test.d.ts +12 -0
- package/dist/src/__tests__/stdlib/result.test.js +329 -0
- package/dist/src/__tests__/stdlib/scheduler.test.js +19 -8
- package/dist/src/__tests__/stdlib/set_int.test.d.ts +1 -0
- package/dist/src/__tests__/stdlib/set_int.test.js +88 -0
- package/dist/src/__tests__/stdlib/sets.test.d.ts +6 -0
- package/dist/src/__tests__/stdlib/sets.test.js +60 -0
- package/dist/src/__tests__/stdlib/signal.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/signal.test.js +84 -0
- package/dist/src/__tests__/stdlib/spawn.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/spawn.test.js +68 -0
- package/dist/src/__tests__/stdlib/string.test.d.ts +12 -0
- package/dist/src/__tests__/stdlib/string.test.js +231 -0
- package/dist/src/__tests__/stdlib/strings.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/strings.test.js +83 -0
- package/dist/src/__tests__/stdlib/tags.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/tags.test.js +57 -0
- package/dist/src/__tests__/stdlib/teams.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/teams.test.js +72 -0
- package/dist/src/__tests__/stdlib/timer.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/timer.test.js +79 -0
- package/dist/src/__tests__/stdlib/vec.test.d.ts +5 -0
- package/dist/src/__tests__/stdlib/vec.test.js +94 -0
- package/dist/src/__tests__/stdlib/world.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib/world.test.js +72 -0
- package/dist/src/__tests__/struct-display.test.d.ts +1 -0
- package/dist/src/__tests__/struct-display.test.js +64 -0
- package/dist/src/__tests__/test-framework/runner.test.d.ts +10 -0
- package/dist/src/__tests__/test-framework/runner.test.js +193 -0
- package/dist/src/__tests__/tuner/adapters.test.d.ts +14 -0
- package/dist/src/__tests__/tuner/adapters.test.js +194 -0
- package/dist/src/__tests__/tuner/simulator-extra.test.d.ts +4 -0
- package/dist/src/__tests__/tuner/simulator-extra.test.js +193 -0
- package/dist/src/__tests__/typechecker-coverage.test.d.ts +30 -0
- package/dist/src/__tests__/typechecker-coverage.test.js +627 -0
- package/dist/src/__tests__/typechecker.test.js +3 -3
- package/dist/src/__tests__/watch-decorator.test.d.ts +1 -0
- package/dist/src/__tests__/watch-decorator.test.js +54 -0
- package/dist/src/ast/types.d.ts +102 -3
- package/dist/src/cache/incremental.d.ts +13 -14
- package/dist/src/cache/incremental.js +106 -89
- package/dist/src/cache/index.d.ts +8 -2
- package/dist/src/cache/index.js +18 -6
- package/dist/src/cli.d.ts +1 -0
- package/dist/src/cli.js +466 -17
- package/dist/src/config/project-config.d.ts +29 -0
- package/dist/src/config/project-config.js +180 -0
- package/dist/src/diagnostics/index.d.ts +9 -0
- package/dist/src/diagnostics/index.js +18 -1
- package/dist/src/emit/compile.d.ts +10 -0
- package/dist/src/emit/compile.js +395 -50
- package/dist/src/emit/index.d.ts +40 -0
- package/dist/src/emit/index.js +307 -14
- package/dist/src/emit/modules.js +21 -3
- package/dist/src/emit/sourcemap.d.ts +23 -27
- package/dist/src/emit/sourcemap.js +52 -30
- package/dist/src/formatter/index.js +33 -8
- package/dist/src/hir/deprecated.d.ts +13 -0
- package/dist/src/hir/deprecated.js +218 -0
- package/dist/src/hir/lower.js +114 -8
- package/dist/src/hir/monomorphize.js +22 -2
- package/dist/src/hir/types.d.ts +65 -1
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.js +18 -3
- package/dist/src/lexer/index.d.ts +2 -1
- package/dist/src/lexer/index.js +39 -3
- package/dist/src/lint/index.d.ts +45 -0
- package/dist/src/lint/index.js +930 -0
- package/dist/src/lir/lower.js +29 -2
- package/dist/src/lir/types.d.ts +2 -0
- package/dist/src/lsp/server.js +92 -5
- package/dist/src/mir/lower.js +775 -34
- package/dist/src/mir/macro.js +36 -2
- package/dist/src/mir/types.d.ts +12 -0
- package/dist/src/mir/verify.js +9 -0
- package/dist/src/optimizer/auto-inline.d.ts +2 -0
- package/dist/src/optimizer/auto-inline.js +67 -0
- package/dist/src/optimizer/cse.d.ts +20 -0
- package/dist/src/optimizer/cse.js +234 -0
- package/dist/src/optimizer/inline.d.ts +26 -0
- package/dist/src/optimizer/inline.js +286 -0
- package/dist/src/optimizer/interprocedural.js +4 -0
- package/dist/src/optimizer/licm.d.ts +32 -0
- package/dist/src/optimizer/licm.js +371 -0
- package/dist/src/optimizer/pipeline.js +12 -2
- package/dist/src/optimizer/strength_reduction.d.ts +15 -0
- package/dist/src/optimizer/strength_reduction.js +90 -0
- package/dist/src/optimizer/tco.d.ts +53 -0
- package/dist/src/optimizer/tco.js +238 -0
- package/dist/src/parser/index.d.ts +32 -0
- package/dist/src/parser/index.js +421 -59
- package/dist/src/repl-server.d.ts +13 -0
- package/dist/src/repl-server.js +127 -0
- package/dist/src/structs/expand.d.ts +15 -0
- package/dist/src/structs/expand.js +46 -0
- package/dist/src/testing/runner.d.ts +40 -0
- package/dist/src/testing/runner.js +237 -0
- package/dist/src/typechecker/index.d.ts +3 -0
- package/dist/src/typechecker/index.js +254 -9
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/doc-drafts/redscript-docs/docs/en/stdlib/graph.md +104 -0
- package/doc-drafts/redscript-docs/docs/en/stdlib/parabola.md +113 -0
- package/doc-drafts/redscript-docs/docs/en/stdlib/pathfind.md +104 -0
- package/doc-drafts/redscript-docs/docs/en/stdlib/physics.md +134 -0
- package/doc-drafts/redscript-docs/docs/en/stdlib/quaternion.md +135 -0
- package/doc-drafts/redscript-docs/docs/zh/stdlib/graph.md +104 -0
- package/doc-drafts/redscript-docs/docs/zh/stdlib/parabola.md +113 -0
- package/doc-drafts/redscript-docs/docs/zh/stdlib/pathfind.md +104 -0
- package/doc-drafts/redscript-docs/docs/zh/stdlib/physics.md +134 -0
- package/doc-drafts/redscript-docs/docs/zh/stdlib/quaternion.md +135 -0
- package/docs/stdlib/result.md +156 -0
- package/docs/stdlib/result.zh.md +156 -0
- package/editors/vscode/fixtures/test.mcrs +7 -0
- package/editors/vscode/out/extension.js +2095 -225
- package/editors/vscode/out/lsp-server.js +519 -51
- package/editors/vscode/package-lock.json +9 -4
- package/editors/vscode/package.json +1 -1
- package/examples/display-demo.mcrs +64 -0
- package/examples/game/racing.mcrs +301 -0
- package/examples/game/tower_defense.mcrs +311 -0
- package/examples/math/physics_sim.mcrs +322 -0
- package/examples/rpg/boss_fight.mcrs +313 -0
- package/examples/rpg/health_system.mcrs +237 -0
- package/examples/rpg/inventory.mcrs +265 -0
- package/examples/util/debug_hud.mcrs +279 -0
- package/jest.config.js +10 -0
- package/package.json +12 -3
- package/playground/index.html +823 -0
- package/scripts/gen-docs.ts +533 -0
- package/scripts/update-redscript-docs-stdlib.sh +770 -0
- package/src/__tests__/budget.test.ts +5 -0
- package/src/__tests__/cache/cache-behavior.test.ts +480 -0
- package/src/__tests__/cache-extra.test.ts +199 -0
- package/src/__tests__/cli-docs.test.ts +77 -0
- package/src/__tests__/cli-init.test.ts +91 -0
- package/src/__tests__/cli-publish.test.ts +190 -0
- package/src/__tests__/cli.test.ts +117 -1
- package/src/__tests__/compile-preprocess.test.ts +366 -0
- package/src/__tests__/compiler/break-stmt.test.ts +66 -0
- package/src/__tests__/compiler/const-decl.test.ts +141 -0
- package/src/__tests__/compiler/continue-stmt.test.ts +81 -0
- package/src/__tests__/compiler/coroutine-extended.test.ts +723 -0
- package/src/__tests__/compiler/deprecated.test.ts +305 -0
- package/src/__tests__/compiler/do-while.test.ts +130 -0
- package/src/__tests__/compiler/enum-payload.test.ts +299 -0
- package/src/__tests__/compiler/interface.test.ts +287 -0
- package/src/__tests__/compiler/labeled-loops.test.ts +279 -0
- package/src/__tests__/compiler/match-string.test.ts +45 -0
- package/src/__tests__/compiler/memoize.test.ts +126 -0
- package/src/__tests__/compiler/method-chain.test.ts +121 -0
- package/src/__tests__/compiler/module-import.test.ts +240 -0
- package/src/__tests__/compiler/option-extensions.test.ts +207 -0
- package/src/__tests__/compiler/profile-decorator.test.ts +79 -0
- package/src/__tests__/compiler/string-advanced.test.ts +310 -0
- package/src/__tests__/compiler/struct-extends.test.ts +109 -0
- package/src/__tests__/compiler/throttle-retry.test.ts +191 -0
- package/src/__tests__/compiler/tuple-type.test.ts +263 -0
- package/src/__tests__/compiler/watch-decorator.test.ts +72 -0
- package/src/__tests__/config/project-config.test.ts +181 -0
- package/src/__tests__/config-decorator.test.ts +157 -0
- package/src/__tests__/diagnostics-extra.test.ts +155 -0
- package/src/__tests__/emit/compile-branches.test.ts +135 -0
- package/src/__tests__/emit/compile-coverage.test.ts +696 -0
- package/src/__tests__/emit/compile-extra-branches.test.ts +228 -0
- package/src/__tests__/emit/compile-mocked-branches.test.ts +249 -0
- package/src/__tests__/emit/compile.test.ts +6 -1
- package/src/__tests__/emit/execute-chain.test.ts +114 -0
- package/src/__tests__/emit/index.test.ts +2 -1
- package/src/__tests__/emit/modules-branches.test.ts +90 -0
- package/src/__tests__/emit/modules-coverage.test.ts +241 -0
- package/src/__tests__/emit/modules-errors.test.ts +192 -0
- package/src/__tests__/emit/modules-rewrite.test.ts +232 -0
- package/src/__tests__/emit/source-map.test.ts +152 -0
- package/src/__tests__/enum.test.ts +9 -4
- package/src/__tests__/error-recovery.test.ts +226 -0
- package/src/__tests__/events-types-extra.test.ts +110 -0
- package/src/__tests__/events-types.test.ts +66 -0
- package/src/__tests__/formatter.test.ts +15 -5
- package/src/__tests__/generics.test.ts +16 -9
- package/src/__tests__/hir/lower-extra.test.ts +151 -0
- package/src/__tests__/hir/monomorphize-coverage.test.ts +432 -0
- package/src/__tests__/hir/monomorphize-extra.test.ts +220 -0
- package/src/__tests__/hir/monomorphize-extra2.test.ts +350 -0
- package/src/__tests__/impl.test.ts +12 -8
- package/src/__tests__/incremental.test.ts +10 -2
- package/src/__tests__/index-extra.test.ts +79 -0
- package/src/__tests__/lexer.test.ts +2 -2
- package/src/__tests__/lint/hir-coverage.test.ts +1716 -0
- package/src/__tests__/lint/rules-coverage.test.ts +598 -0
- package/src/__tests__/lint/rules.test.ts +230 -0
- package/src/__tests__/lir/lower.test.ts +33 -0
- package/src/__tests__/lir/verify.test.ts +33 -0
- package/src/__tests__/lsp/completion.test.ts +687 -0
- package/src/__tests__/lsp/definition.test.ts +499 -0
- package/src/__tests__/lsp/diagnostics.test.ts +108 -0
- package/src/__tests__/lsp/hover-docs.test.ts +222 -0
- package/src/__tests__/lsp.test.ts +4 -1
- package/src/__tests__/mc-integration/item-entity-events.test.ts +5 -0
- package/src/__tests__/mc-integration/stdlib-coverage-2.test.ts +5 -0
- package/src/__tests__/mc-integration/stdlib-coverage-3.test.ts +1105 -0
- package/src/__tests__/mc-integration/stdlib-coverage-4.test.ts +1366 -0
- package/src/__tests__/mc-integration/stdlib-coverage-5.test.ts +1245 -0
- package/src/__tests__/mc-integration/stdlib-coverage-6.test.ts +755 -0
- package/src/__tests__/mc-integration/stdlib-coverage-7.test.ts +771 -0
- package/src/__tests__/mc-integration/stdlib-coverage.test.ts +5 -0
- package/src/__tests__/mc-integration/syntax-coverage.test.ts +5 -0
- package/src/__tests__/mc-validator-coverage.test.ts +325 -0
- package/src/__tests__/mc-validator-extra.test.ts +252 -0
- package/src/__tests__/mir/lower-extra.test.ts +402 -0
- package/src/__tests__/mir/lower-extra2.test.ts +348 -0
- package/src/__tests__/mir/lower-extra3.test.ts +277 -0
- package/src/__tests__/mir/lower-extra4.test.ts +636 -0
- package/src/__tests__/mir/lower-extra5.test.ts +612 -0
- package/src/__tests__/mir/lower-extra6.test.ts +520 -0
- package/src/__tests__/mir/lower-extra7.test.ts +1045 -0
- package/src/__tests__/mir/lower-extra8.test.ts +704 -0
- package/src/__tests__/mir/lower-extra9.test.ts +821 -0
- package/src/__tests__/optimizer/auto-inline.test.ts +206 -0
- package/src/__tests__/optimizer/cse.test.ts +195 -0
- package/src/__tests__/optimizer/inline_fn.test.ts +263 -0
- package/src/__tests__/optimizer/licm.test.ts +358 -0
- package/src/__tests__/optimizer/nbt-coalesce.test.ts +147 -0
- package/src/__tests__/optimizer/optimizer-extended.test.ts +1081 -0
- package/src/__tests__/optimizer/scoreboard-batch.test.ts +141 -0
- package/src/__tests__/optimizer/strength-reduction.test.ts +111 -0
- package/src/__tests__/optimizer/tco-coverage.test.ts +309 -0
- package/src/__tests__/optimizer/tco.test.ts +238 -0
- package/src/__tests__/option.test.ts +14 -7
- package/src/__tests__/parser-coverage.test.ts +576 -0
- package/src/__tests__/parser-extra.test.ts +531 -0
- package/src/__tests__/parser.test.ts +14 -0
- package/src/__tests__/repl-extra.test.ts +195 -0
- package/src/__tests__/repl-server-extra.test.ts +150 -0
- package/src/__tests__/repl-server.test.ts +122 -0
- package/src/__tests__/runtime-extra.test.ts +862 -0
- package/src/__tests__/singleton-decorator.test.ts +285 -0
- package/src/__tests__/sourcemap.test.ts +1 -1
- package/src/__tests__/stdlib/advanced.test.ts +312 -0
- package/src/__tests__/stdlib/bigint.test.ts +57 -0
- package/src/__tests__/stdlib/bits.test.ts +75 -0
- package/src/__tests__/stdlib/bossbar.test.ts +45 -0
- package/src/__tests__/stdlib/color.test.ts +60 -0
- package/src/__tests__/stdlib/combat.test.ts +35 -0
- package/src/__tests__/stdlib/cooldown.test.ts +35 -0
- package/src/__tests__/stdlib/dialog.test.ts +14 -6
- package/src/__tests__/stdlib/ecs.test.ts +54 -0
- package/src/__tests__/stdlib/effects.test.ts +45 -0
- package/src/__tests__/stdlib/events.test.ts +23 -0
- package/src/__tests__/stdlib/expr.test.ts +48 -0
- package/src/__tests__/stdlib/fft.test.ts +54 -0
- package/src/__tests__/stdlib/graph.test.ts +77 -0
- package/src/__tests__/stdlib/interactions.test.ts +30 -0
- package/src/__tests__/stdlib/inventory.test.ts +40 -0
- package/src/__tests__/stdlib/linalg.test.ts +52 -0
- package/src/__tests__/stdlib/map.test.ts +55 -0
- package/src/__tests__/stdlib/math.test.ts +19 -5
- package/src/__tests__/stdlib/math_hp.test.ts +55 -0
- package/src/__tests__/stdlib/mobs.test.ts +40 -0
- package/src/__tests__/stdlib/noise.test.ts +46 -0
- package/src/__tests__/stdlib/ode.test.ts +40 -0
- package/src/__tests__/stdlib/parabola.test.ts +51 -0
- package/src/__tests__/stdlib/particles.test.ts +40 -0
- package/src/__tests__/stdlib/physics.test.ts +50 -0
- package/src/__tests__/stdlib/player.test.ts +35 -0
- package/src/__tests__/stdlib/quaternion.test.ts +46 -0
- package/src/__tests__/stdlib/queue.test.ts +73 -0
- package/src/__tests__/stdlib/random.test.ts +50 -0
- package/src/__tests__/stdlib/result.test.ts +326 -0
- package/src/__tests__/stdlib/scheduler.test.ts +18 -7
- package/src/__tests__/stdlib/set_int.test.ts +62 -0
- package/src/__tests__/stdlib/sets.test.ts +28 -0
- package/src/__tests__/stdlib/signal.test.ts +60 -0
- package/src/__tests__/stdlib/spawn.test.ts +40 -0
- package/src/__tests__/stdlib/string.test.ts +224 -0
- package/src/__tests__/stdlib/strings.test.ts +55 -0
- package/src/__tests__/stdlib/tags.test.ts +32 -0
- package/src/__tests__/stdlib/teams.test.ts +45 -0
- package/src/__tests__/stdlib/timer.test.ts +53 -0
- package/src/__tests__/stdlib/vec.test.ts +72 -0
- package/src/__tests__/stdlib/world.test.ts +45 -0
- package/src/__tests__/struct-display.test.ts +69 -0
- package/src/__tests__/test-framework/runner.test.ts +208 -0
- package/src/__tests__/tuner/adapters.test.ts +232 -0
- package/src/__tests__/tuner/simulator-extra.test.ts +222 -0
- package/src/__tests__/tuple.test.ts +11 -4
- package/src/__tests__/typechecker-coverage.test.ts +671 -0
- package/src/__tests__/typechecker.test.ts +4 -3
- package/src/__tests__/watch-decorator.test.ts +59 -0
- package/src/ast/types.ts +65 -3
- package/src/cache/incremental.ts +128 -99
- package/src/cache/index.ts +35 -8
- package/src/cli.ts +538 -29
- package/src/config/project-config.ts +176 -0
- package/src/diagnostics/index.ts +22 -0
- package/src/docs.ts +98 -0
- package/src/emit/compile.ts +408 -51
- package/src/emit/index.ts +366 -18
- package/src/emit/modules.ts +19 -3
- package/src/emit/sourcemap.ts +64 -43
- package/src/formatter/index.ts +35 -8
- package/src/hir/deprecated.ts +212 -0
- package/src/hir/lower.ts +128 -8
- package/src/hir/monomorphize.ts +24 -2
- package/src/hir/types.ts +26 -1
- package/src/index.ts +23 -3
- package/src/lexer/index.ts +45 -6
- package/src/lint/index.ts +922 -0
- package/src/lir/lower.ts +30 -2
- package/src/lir/types.ts +4 -0
- package/src/lsp/server.ts +100 -1
- package/src/mir/lower.ts +785 -40
- package/src/mir/macro.ts +30 -2
- package/src/mir/types.ts +13 -0
- package/src/mir/verify.ts +10 -2
- package/src/optimizer/auto-inline.ts +86 -0
- package/src/optimizer/copy_prop.ts +2 -2
- package/src/optimizer/coroutine.ts +3 -3
- package/src/optimizer/cse.ts +205 -0
- package/src/optimizer/dce.ts +2 -2
- package/src/optimizer/inline.ts +335 -0
- package/src/optimizer/interprocedural.ts +5 -1
- package/src/optimizer/licm.ts +454 -0
- package/src/optimizer/nbt-coalesce.ts +109 -0
- package/src/optimizer/pipeline.ts +16 -2
- package/src/optimizer/scoreboard-batch.ts +52 -0
- package/src/optimizer/strength_reduction.ts +95 -0
- package/src/optimizer/tco.ts +267 -0
- package/src/optimizer/unroll.ts +2 -2
- package/src/parser/index.ts +426 -53
- package/src/repl-server.ts +102 -0
- package/src/stdlib/advanced.mcrs +271 -101
- package/src/stdlib/bigint.mcrs +97 -11
- package/src/stdlib/bits.mcrs +75 -12
- package/src/stdlib/bossbar.mcrs +37 -8
- package/src/stdlib/calculus.mcrs +82 -26
- package/src/stdlib/color.mcrs +98 -16
- package/src/stdlib/combat.mcrs +23 -5
- package/src/stdlib/cooldown.mcrs +19 -0
- package/src/stdlib/dialog.mcrs +45 -7
- package/src/stdlib/easing.mcrs +132 -12
- package/src/stdlib/ecs.mcrs +142 -25
- package/src/stdlib/effects.mcrs +88 -12
- package/src/stdlib/events.mcrs +21 -2
- package/src/stdlib/expr.mcrs +18 -3
- package/src/stdlib/fft.mcrs +66 -56
- package/src/stdlib/geometry.mcrs +137 -39
- package/src/stdlib/graph.mcrs +73 -0
- package/src/stdlib/heap.mcrs +49 -8
- package/src/stdlib/i18n/zh.yaml +2891 -0
- package/src/stdlib/interactions.mcrs +43 -20
- package/src/stdlib/inventory.mcrs +14 -3
- package/src/stdlib/linalg.mcrs +185 -30
- package/src/stdlib/list.mcrs +168 -18
- package/src/stdlib/map.mcrs +112 -0
- package/src/stdlib/math.mcrs +68 -18
- package/src/stdlib/math_hp.mcrs +124 -33
- package/src/stdlib/matrix.mcrs +133 -20
- package/src/stdlib/mobs.mcrs +87 -0
- package/src/stdlib/noise.mcrs +65 -21
- package/src/stdlib/ode.mcrs +96 -0
- package/src/stdlib/parabola.mcrs +104 -29
- package/src/stdlib/particles.mcrs +78 -21
- package/src/stdlib/pathfind.mcrs +89 -35
- package/src/stdlib/physics.mcrs +134 -26
- package/src/stdlib/player.mcrs +18 -0
- package/src/stdlib/quaternion.mcrs +213 -9
- package/src/stdlib/queue.mcrs +123 -0
- package/src/stdlib/random.mcrs +63 -18
- package/src/stdlib/result.mcrs +111 -0
- package/src/stdlib/scheduler.mcrs +59 -10
- package/src/stdlib/set_int.mcrs +240 -0
- package/src/stdlib/sets.mcrs +49 -19
- package/src/stdlib/signal.mcrs +151 -79
- package/src/stdlib/sort.mcrs +44 -24
- package/src/stdlib/spawn.mcrs +30 -7
- package/src/stdlib/state.mcrs +40 -5
- package/src/stdlib/strings.mcrs +131 -3
- package/src/stdlib/tags.mcrs +2 -2
- package/src/stdlib/teams.mcrs +22 -10
- package/src/stdlib/timer.mcrs +36 -6
- package/src/stdlib/vec.mcrs +44 -9
- package/src/stdlib/world.mcrs +57 -25
- package/src/structs/expand.ts +64 -0
- package/src/testing/runner.ts +271 -0
- package/src/typechecker/index.ts +273 -9
package/src/mir/lower.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import type {
|
|
9
9
|
HIRModule, HIRFunction, HIRStmt, HIRBlock, HIRExpr,
|
|
10
|
-
HIRExecuteSubcommand, HIRParam,
|
|
10
|
+
HIRExecuteSubcommand, HIRParam, TypeNode, HIRFStringPart,
|
|
11
11
|
} from '../hir/types'
|
|
12
12
|
import type {
|
|
13
13
|
MIRModule, MIRFunction, MIRBlock, MIRInstr, BlockId,
|
|
@@ -15,6 +15,35 @@ import type {
|
|
|
15
15
|
} from './types'
|
|
16
16
|
import { detectMacroFunctions, BUILTIN_SET, type MacroFunctionInfo } from './macro'
|
|
17
17
|
|
|
18
|
+
function formatTypeNode(type: TypeNode): string {
|
|
19
|
+
switch (type.kind) {
|
|
20
|
+
case 'named':
|
|
21
|
+
return type.name
|
|
22
|
+
case 'array':
|
|
23
|
+
return `${formatTypeNode(type.elem)}[]`
|
|
24
|
+
case 'struct':
|
|
25
|
+
case 'enum':
|
|
26
|
+
return type.name
|
|
27
|
+
case 'function_type':
|
|
28
|
+
return `fn(${type.params.map(formatTypeNode).join(', ')}) -> ${formatTypeNode(type.return)}`
|
|
29
|
+
case 'entity':
|
|
30
|
+
return type.entityType
|
|
31
|
+
case 'selector':
|
|
32
|
+
return type.entityType ? `selector<${type.entityType}>` : 'selector'
|
|
33
|
+
case 'tuple':
|
|
34
|
+
return `(${type.elements.map(formatTypeNode).join(', ')})`
|
|
35
|
+
case 'option':
|
|
36
|
+
return `Option<${formatTypeNode(type.inner)}>`
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function formatFunctionSignature(fn: HIRFunction): string {
|
|
41
|
+
const params = fn.params
|
|
42
|
+
.map(param => `${param.name}: ${formatTypeNode(param.type)}`)
|
|
43
|
+
.join(', ')
|
|
44
|
+
return `fn ${fn.name}(${params}) -> ${formatTypeNode(fn.returnType)}`
|
|
45
|
+
}
|
|
46
|
+
|
|
18
47
|
// ---------------------------------------------------------------------------
|
|
19
48
|
// Public API
|
|
20
49
|
// ---------------------------------------------------------------------------
|
|
@@ -22,31 +51,59 @@ import { detectMacroFunctions, BUILTIN_SET, type MacroFunctionInfo } from './mac
|
|
|
22
51
|
export function lowerToMIR(hir: HIRModule, sourceFile?: string): MIRModule {
|
|
23
52
|
// Build struct definitions: name → field names
|
|
24
53
|
const structDefs = new Map<string, string[]>()
|
|
54
|
+
// Track @singleton struct names for special expansion of GameState::set(gs) calls
|
|
55
|
+
const singletonStructs = new Set<string>()
|
|
25
56
|
for (const s of hir.structs) {
|
|
26
57
|
structDefs.set(s.name, s.fields.map(f => f.name))
|
|
58
|
+
if (s.isSingleton) singletonStructs.add(s.name)
|
|
27
59
|
}
|
|
28
60
|
|
|
29
61
|
// Build enum definitions: enumName → variantName → integer value
|
|
30
62
|
const enumDefs = new Map<string, Map<string, number>>()
|
|
63
|
+
// Build enum payload info: enumName → variantName → field list
|
|
64
|
+
const enumPayloads = new Map<string, Map<string, { name: string; type: TypeNode }[]>>()
|
|
31
65
|
for (const e of hir.enums) {
|
|
32
66
|
const variants = new Map<string, number>()
|
|
67
|
+
const payloads = new Map<string, { name: string; type: TypeNode }[]>()
|
|
33
68
|
for (const v of e.variants) {
|
|
34
69
|
variants.set(v.name, v.value ?? 0)
|
|
70
|
+
if (v.fields && v.fields.length > 0) {
|
|
71
|
+
payloads.set(v.name, v.fields)
|
|
72
|
+
}
|
|
35
73
|
}
|
|
36
74
|
enumDefs.set(e.name, variants)
|
|
75
|
+
if (payloads.size > 0) {
|
|
76
|
+
enumPayloads.set(e.name, payloads)
|
|
77
|
+
}
|
|
37
78
|
}
|
|
38
79
|
|
|
39
|
-
// Build impl method info: typeName → methodName → { hasSelf }
|
|
40
|
-
const implMethods = new Map<string, Map<string, { hasSelf: boolean }>>()
|
|
80
|
+
// Build impl method info: typeName → methodName → { hasSelf, returnStructName? }
|
|
81
|
+
const implMethods = new Map<string, Map<string, { hasSelf: boolean; returnStructName?: string }>>()
|
|
41
82
|
for (const ib of hir.implBlocks) {
|
|
42
|
-
const methods = new Map<string, { hasSelf: boolean }>()
|
|
83
|
+
const methods = new Map<string, { hasSelf: boolean; returnStructName?: string }>()
|
|
43
84
|
for (const m of ib.methods) {
|
|
44
85
|
const hasSelf = m.params.length > 0 && m.params[0].name === 'self'
|
|
45
|
-
|
|
86
|
+
const returnStructName = m.returnType.kind === 'struct' ? m.returnType.name : undefined
|
|
87
|
+
methods.set(m.name, { hasSelf, returnStructName })
|
|
46
88
|
}
|
|
47
89
|
implMethods.set(ib.typeName, methods)
|
|
48
90
|
}
|
|
49
91
|
|
|
92
|
+
// Build Display impl registry: typeName → f-string parts from to_string body
|
|
93
|
+
// Used to inline Display::to_string() calls at call sites instead of generating a function.
|
|
94
|
+
const displayImpls = new Map<string, HIRFStringPart[]>()
|
|
95
|
+
for (const ib of hir.implBlocks) {
|
|
96
|
+
if (ib.traitName === 'Display') {
|
|
97
|
+
const toStringMethod = ib.methods.find(m => m.name === 'to_string')
|
|
98
|
+
if (toStringMethod && toStringMethod.body.length > 0) {
|
|
99
|
+
const firstStmt = toStringMethod.body[0]
|
|
100
|
+
if (firstStmt.kind === 'return' && firstStmt.value && firstStmt.value.kind === 'f_string') {
|
|
101
|
+
displayImpls.set(ib.typeName, firstStmt.value.parts)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
50
107
|
// Pre-scan for macro functions
|
|
51
108
|
const macroInfo = detectMacroFunctions(hir)
|
|
52
109
|
|
|
@@ -71,16 +128,25 @@ export function lowerToMIR(hir: HIRModule, sourceFile?: string): MIRModule {
|
|
|
71
128
|
// Shared registry: specializedName → [mirFn, ...helpers]
|
|
72
129
|
const specializedFnsRegistry = new Map<string, MIRFunction[]>()
|
|
73
130
|
|
|
131
|
+
// Build module-level const map: name → integer value (for inlining at use sites)
|
|
132
|
+
const constValues = new Map<string, number>()
|
|
133
|
+
for (const c of hir.consts) {
|
|
134
|
+
if (c.value.kind === 'int_lit') constValues.set(c.name, c.value.value)
|
|
135
|
+
else if (c.value.kind === 'bool_lit') constValues.set(c.name, c.value.value ? 1 : 0)
|
|
136
|
+
else if (c.value.kind === 'float_lit') constValues.set(c.name, Math.round(c.value.value * 10000))
|
|
137
|
+
}
|
|
138
|
+
|
|
74
139
|
const allFunctions: MIRFunction[] = []
|
|
75
140
|
for (const f of hir.functions) {
|
|
76
|
-
const { fn, helpers } = lowerFunction(f, hir.namespace, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, sourceFile, timerCounter, undefined, hirFnMap, specializedFnsRegistry)
|
|
141
|
+
const { fn, helpers } = lowerFunction(f, hir.namespace, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, sourceFile, timerCounter, undefined, hirFnMap, specializedFnsRegistry, undefined, enumPayloads, constValues, singletonStructs, displayImpls)
|
|
77
142
|
allFunctions.push(fn, ...helpers)
|
|
78
143
|
}
|
|
79
144
|
|
|
80
|
-
// Lower impl block methods
|
|
145
|
+
// Lower impl block methods (skip Display::to_string — inlined at call sites instead)
|
|
81
146
|
for (const ib of hir.implBlocks) {
|
|
147
|
+
if (ib.traitName === 'Display') continue // Display impls are inlined, not emitted as functions
|
|
82
148
|
for (const m of ib.methods) {
|
|
83
|
-
const { fn, helpers } = lowerImplMethod(m, ib.typeName, hir.namespace, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, sourceFile, timerCounter)
|
|
149
|
+
const { fn, helpers } = lowerImplMethod(m, ib.typeName, hir.namespace, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, sourceFile, timerCounter, enumPayloads, constValues)
|
|
84
150
|
allFunctions.push(fn, ...helpers)
|
|
85
151
|
}
|
|
86
152
|
}
|
|
@@ -106,22 +172,26 @@ class FnContext {
|
|
|
106
172
|
private blockCounter = 0
|
|
107
173
|
readonly blocks: MIRBlock[] = []
|
|
108
174
|
private currentBlock: MIRBlock
|
|
109
|
-
/** Stack of (loopHeader, loopExit, continueTo) for break/continue */
|
|
110
|
-
private loopStack: { header: BlockId; exit: BlockId; continueTo: BlockId }[] = []
|
|
175
|
+
/** Stack of (loopHeader, loopExit, continueTo, label?) for break/continue */
|
|
176
|
+
private loopStack: { header: BlockId; exit: BlockId; continueTo: BlockId; label?: string }[] = []
|
|
177
|
+
/** Pending label to attach to the next pushLoop call (set by labeled_loop lowering) */
|
|
178
|
+
private pendingLoopLabel: string | undefined = undefined
|
|
111
179
|
/** Extracted helper functions for execute blocks */
|
|
112
180
|
readonly helperFunctions: MIRFunction[] = []
|
|
113
181
|
private readonly namespace: string
|
|
114
182
|
private readonly fnName: string
|
|
115
183
|
/** Struct definitions: struct name → field names */
|
|
116
184
|
readonly structDefs: Map<string, string[]>
|
|
117
|
-
/** Impl method info: typeName → methodName → { hasSelf } */
|
|
118
|
-
readonly implMethods: Map<string, Map<string, { hasSelf: boolean }>>
|
|
185
|
+
/** Impl method info: typeName → methodName → { hasSelf, returnStructName? } */
|
|
186
|
+
readonly implMethods: Map<string, Map<string, { hasSelf: boolean; returnStructName?: string }>>
|
|
119
187
|
/** Struct variable tracking: varName → { typeName, fields: fieldName → temp } */
|
|
120
188
|
readonly structVars = new Map<string, { typeName: string; fields: Map<string, Temp> }>()
|
|
121
189
|
/** Tuple variable tracking: varName → array of element temps (index = slot) */
|
|
122
190
|
readonly tupleVars = new Map<string, Temp[]>()
|
|
123
191
|
/** Array variable tracking: varName → { ns, pathPrefix } for NBT-backed int[] */
|
|
124
192
|
readonly arrayVars = new Map<string, { ns: string; pathPrefix: string; knownLen?: number }>()
|
|
193
|
+
/** String variable tracking: varName → storage path within rs:strings */
|
|
194
|
+
readonly stringVars = new Map<string, string>()
|
|
125
195
|
/** Macro function info for all functions in the module */
|
|
126
196
|
readonly macroInfo: Map<string, MacroFunctionInfo>
|
|
127
197
|
/** Function parameter info for call_macro generation */
|
|
@@ -130,6 +200,8 @@ class FnContext {
|
|
|
130
200
|
readonly currentMacroParams: Set<string>
|
|
131
201
|
/** Enum definitions: enumName → variantName → integer value */
|
|
132
202
|
readonly enumDefs: Map<string, Map<string, number>>
|
|
203
|
+
/** Enum payload fields: enumName → variantName → field list */
|
|
204
|
+
readonly enumPayloads: Map<string, Map<string, { name: string; type: TypeNode }[]>>
|
|
133
205
|
/** Current source location (set during statement lowering) */
|
|
134
206
|
currentSourceLoc: SourceLoc | undefined = undefined
|
|
135
207
|
/** Source file path for the module being compiled */
|
|
@@ -143,20 +215,28 @@ class FnContext {
|
|
|
143
215
|
/** Tracks double variables: varName → NBT storage path (without "rs:d " prefix) */
|
|
144
216
|
readonly doubleVars = new Map<string, string>()
|
|
145
217
|
private doubleVarCount = 0
|
|
218
|
+
private stringVarCount = 0
|
|
146
219
|
/** HIR function definitions for array-arg monomorphization */
|
|
147
220
|
hirFunctions: Map<string, HIRFunction> = new Map()
|
|
148
221
|
/** Shared registry of already-generated specialized (monomorphized) MIR functions */
|
|
149
222
|
specializedFnsRegistry: Map<string, MIRFunction[]> = new Map()
|
|
223
|
+
/** Module-level const values: name → integer value (inlined at use sites) */
|
|
224
|
+
constValues: Map<string, number> = new Map()
|
|
225
|
+
/** @singleton struct names — static_call GameState::set(gs) expands struct fields */
|
|
226
|
+
singletonStructs: Set<string> = new Set()
|
|
227
|
+
/** Display trait impls: typeName → f-string parts from to_string body (inlined at call sites) */
|
|
228
|
+
displayImpls: Map<string, HIRFStringPart[]> = new Map()
|
|
150
229
|
|
|
151
230
|
constructor(
|
|
152
231
|
namespace: string,
|
|
153
232
|
fnName: string,
|
|
154
233
|
structDefs: Map<string, string[]> = new Map(),
|
|
155
|
-
implMethods: Map<string, Map<string, { hasSelf: boolean }>> = new Map(),
|
|
234
|
+
implMethods: Map<string, Map<string, { hasSelf: boolean; returnStructName?: string }>> = new Map(),
|
|
156
235
|
macroInfo: Map<string, MacroFunctionInfo> = new Map(),
|
|
157
236
|
fnParamInfo: Map<string, HIRParam[]> = new Map(),
|
|
158
237
|
enumDefs: Map<string, Map<string, number>> = new Map(),
|
|
159
238
|
timerCounter: { count: number; timerId: number } = { count: 0, timerId: 0 },
|
|
239
|
+
enumPayloads: Map<string, Map<string, { name: string; type: TypeNode }[]>> = new Map(),
|
|
160
240
|
) {
|
|
161
241
|
this.namespace = namespace
|
|
162
242
|
this.fnName = fnName
|
|
@@ -166,6 +246,7 @@ class FnContext {
|
|
|
166
246
|
this.fnParamInfo = fnParamInfo
|
|
167
247
|
this.currentMacroParams = macroInfo.get(fnName)?.macroParams ?? new Set()
|
|
168
248
|
this.enumDefs = enumDefs
|
|
249
|
+
this.enumPayloads = enumPayloads
|
|
169
250
|
this.timerCounter = timerCounter
|
|
170
251
|
const entry = this.makeBlock('entry')
|
|
171
252
|
this.currentBlock = entry
|
|
@@ -212,18 +293,32 @@ class FnContext {
|
|
|
212
293
|
return this.currentBlock
|
|
213
294
|
}
|
|
214
295
|
|
|
215
|
-
|
|
216
|
-
this.
|
|
296
|
+
setPendingLoopLabel(label: string): void {
|
|
297
|
+
this.pendingLoopLabel = label
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
pushLoop(header: BlockId, exit: BlockId, continueTo?: BlockId, label?: string): void {
|
|
301
|
+
const effectiveLabel = label ?? this.pendingLoopLabel
|
|
302
|
+
this.pendingLoopLabel = undefined
|
|
303
|
+
this.loopStack.push({ header, exit, continueTo: continueTo ?? header, label: effectiveLabel })
|
|
217
304
|
}
|
|
218
305
|
|
|
219
306
|
popLoop(): void {
|
|
220
307
|
this.loopStack.pop()
|
|
221
308
|
}
|
|
222
309
|
|
|
223
|
-
currentLoop(): { header: BlockId; exit: BlockId; continueTo: BlockId } | undefined {
|
|
310
|
+
currentLoop(): { header: BlockId; exit: BlockId; continueTo: BlockId; label?: string } | undefined {
|
|
224
311
|
return this.loopStack[this.loopStack.length - 1]
|
|
225
312
|
}
|
|
226
313
|
|
|
314
|
+
/** Find loop by label — searches from innermost to outermost */
|
|
315
|
+
findLoopByLabel(label: string): { header: BlockId; exit: BlockId; continueTo: BlockId } | undefined {
|
|
316
|
+
for (let i = this.loopStack.length - 1; i >= 0; i--) {
|
|
317
|
+
if (this.loopStack[i].label === label) return this.loopStack[i]
|
|
318
|
+
}
|
|
319
|
+
return undefined
|
|
320
|
+
}
|
|
321
|
+
|
|
227
322
|
getNamespace(): string {
|
|
228
323
|
return this.namespace
|
|
229
324
|
}
|
|
@@ -238,6 +333,11 @@ class FnContext {
|
|
|
238
333
|
this.doubleVars.set(varName, path)
|
|
239
334
|
return path
|
|
240
335
|
}
|
|
336
|
+
|
|
337
|
+
/** Allocate a unique NBT storage path for a string value */
|
|
338
|
+
freshStringVar(varName: string): string {
|
|
339
|
+
return `${this.namespace}_${this.fnName}_${varName}_${this.stringVarCount++}`
|
|
340
|
+
}
|
|
241
341
|
}
|
|
242
342
|
|
|
243
343
|
// ---------------------------------------------------------------------------
|
|
@@ -248,7 +348,7 @@ function lowerFunction(
|
|
|
248
348
|
fn: HIRFunction,
|
|
249
349
|
namespace: string,
|
|
250
350
|
structDefs: Map<string, string[]> = new Map(),
|
|
251
|
-
implMethods: Map<string, Map<string, { hasSelf: boolean }>> = new Map(),
|
|
351
|
+
implMethods: Map<string, Map<string, { hasSelf: boolean; returnStructName?: string }>> = new Map(),
|
|
252
352
|
macroInfo: Map<string, MacroFunctionInfo> = new Map(),
|
|
253
353
|
fnParamInfo: Map<string, HIRParam[]> = new Map(),
|
|
254
354
|
enumDefs: Map<string, Map<string, number>> = new Map(),
|
|
@@ -262,10 +362,17 @@ function lowerFunction(
|
|
|
262
362
|
specializedFnsRegistry?: Map<string, MIRFunction[]>,
|
|
263
363
|
/** Override the MIR function name (used when generating specialized versions) */
|
|
264
364
|
overrideName?: string,
|
|
365
|
+
enumPayloads: Map<string, Map<string, { name: string; type: TypeNode }[]>> = new Map(),
|
|
366
|
+
constValues: Map<string, number> = new Map(),
|
|
367
|
+
singletonStructs: Set<string> = new Set(),
|
|
368
|
+
displayImpls: Map<string, HIRFStringPart[]> = new Map(),
|
|
265
369
|
): { fn: MIRFunction; helpers: MIRFunction[] } {
|
|
266
370
|
const mirFnName = overrideName ?? fn.name
|
|
267
|
-
const ctx = new FnContext(namespace, mirFnName, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, timerCounter)
|
|
268
|
-
ctx.sourceFile = sourceFile
|
|
371
|
+
const ctx = new FnContext(namespace, mirFnName, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, timerCounter, enumPayloads)
|
|
372
|
+
ctx.sourceFile = fn.sourceFile ?? sourceFile
|
|
373
|
+
ctx.constValues = constValues
|
|
374
|
+
ctx.singletonStructs = singletonStructs
|
|
375
|
+
ctx.displayImpls = displayImpls
|
|
269
376
|
if (hirFnMap) ctx.hirFunctions = hirFnMap
|
|
270
377
|
if (specializedFnsRegistry) ctx.specializedFnsRegistry = specializedFnsRegistry
|
|
271
378
|
const fnMacroInfo = macroInfo.get(fn.name)
|
|
@@ -282,6 +389,7 @@ function lowerFunction(
|
|
|
282
389
|
const params: { name: Temp; isMacroParam: boolean }[] = []
|
|
283
390
|
const scope = new Map<string, Temp>()
|
|
284
391
|
let doubleParamSlot = 0
|
|
392
|
+
let stringParamSlot = 0
|
|
285
393
|
fn.params.forEach((p) => {
|
|
286
394
|
if (p.type.kind === 'array' && arrayArgBindings?.has(p.name)) {
|
|
287
395
|
// Array param already bound via arrayVars; no scoreboard slot needed
|
|
@@ -294,6 +402,10 @@ function lowerFunction(
|
|
|
294
402
|
// No scoreboard param slot; callee reads from rs:d __dp<i> via doubleVars
|
|
295
403
|
return
|
|
296
404
|
}
|
|
405
|
+
if (p.type.kind === 'named' && (p.type.name === 'string' || p.type.name === 'format_string')) {
|
|
406
|
+
ctx.stringVars.set(p.name, `__sp${stringParamSlot++}`)
|
|
407
|
+
return
|
|
408
|
+
}
|
|
297
409
|
const t = ctx.freshTemp()
|
|
298
410
|
params.push({ name: t, isMacroParam: fnMacroInfo?.macroParams.has(p.name) ?? false })
|
|
299
411
|
scope.set(p.name, t)
|
|
@@ -320,6 +432,8 @@ function lowerFunction(
|
|
|
320
432
|
blocks: liveBlocks,
|
|
321
433
|
entry: 'entry',
|
|
322
434
|
isMacro: fnMacroInfo != null,
|
|
435
|
+
sourceLoc: fn.span && (fn.sourceFile ?? sourceFile) ? { file: fn.sourceFile ?? sourceFile!, line: fn.span.line, col: fn.span.col } : undefined,
|
|
436
|
+
sourceSnippet: formatFunctionSignature(fn),
|
|
323
437
|
}
|
|
324
438
|
|
|
325
439
|
return { fn: result, helpers: ctx.helperFunctions }
|
|
@@ -330,16 +444,19 @@ function lowerImplMethod(
|
|
|
330
444
|
typeName: string,
|
|
331
445
|
namespace: string,
|
|
332
446
|
structDefs: Map<string, string[]>,
|
|
333
|
-
implMethods: Map<string, Map<string, { hasSelf: boolean }>>,
|
|
447
|
+
implMethods: Map<string, Map<string, { hasSelf: boolean; returnStructName?: string }>>,
|
|
334
448
|
macroInfo: Map<string, MacroFunctionInfo> = new Map(),
|
|
335
449
|
fnParamInfo: Map<string, HIRParam[]> = new Map(),
|
|
336
450
|
enumDefs: Map<string, Map<string, number>> = new Map(),
|
|
337
451
|
sourceFile?: string,
|
|
338
452
|
timerCounter: { count: number; timerId: number } = { count: 0, timerId: 0 },
|
|
453
|
+
enumPayloads: Map<string, Map<string, { name: string; type: TypeNode }[]>> = new Map(),
|
|
454
|
+
constValues: Map<string, number> = new Map(),
|
|
339
455
|
): { fn: MIRFunction; helpers: MIRFunction[] } {
|
|
340
456
|
const fnName = `${typeName}::${method.name}`
|
|
341
|
-
const ctx = new FnContext(namespace, fnName, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, timerCounter)
|
|
342
|
-
ctx.sourceFile = sourceFile
|
|
457
|
+
const ctx = new FnContext(namespace, fnName, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, timerCounter, enumPayloads)
|
|
458
|
+
ctx.sourceFile = method.sourceFile ?? sourceFile
|
|
459
|
+
ctx.constValues = constValues
|
|
343
460
|
const fields = structDefs.get(typeName) ?? []
|
|
344
461
|
const hasSelf = method.params.length > 0 && method.params[0].name === 'self'
|
|
345
462
|
|
|
@@ -415,6 +532,8 @@ function lowerImplMethod(
|
|
|
415
532
|
blocks: liveBlocks,
|
|
416
533
|
entry: 'entry',
|
|
417
534
|
isMacro: macroInfo.has(fnName),
|
|
535
|
+
sourceLoc: method.span && (method.sourceFile ?? sourceFile) ? { file: method.sourceFile ?? sourceFile!, line: method.span.line, col: method.span.col } : undefined,
|
|
536
|
+
sourceSnippet: formatFunctionSignature(method),
|
|
418
537
|
}
|
|
419
538
|
|
|
420
539
|
return { fn: result, helpers: ctx.helperFunctions }
|
|
@@ -492,7 +611,13 @@ function lowerStmt(
|
|
|
492
611
|
|
|
493
612
|
switch (stmt.kind) {
|
|
494
613
|
case 'let': {
|
|
495
|
-
if (stmt.
|
|
614
|
+
if (stmt.type?.kind === 'named' && (stmt.type.name === 'string' || stmt.type.name === 'format_string')) {
|
|
615
|
+
const path = lowerStringExprToPath(stmt.init, ctx, scope, stmt.name) ?? ctx.freshStringVar(stmt.name)
|
|
616
|
+
ctx.stringVars.set(stmt.name, path)
|
|
617
|
+
const t = ctx.freshTemp()
|
|
618
|
+
ctx.emit({ kind: 'const', dst: t, value: 0 })
|
|
619
|
+
scope.set(stmt.name, t)
|
|
620
|
+
} else if (stmt.init.kind === 'some_lit') {
|
|
496
621
|
// Some(expr) — create option struct vars: has=1, val=expr
|
|
497
622
|
// Use __opt_ prefix so DCE treats these as side-effectful scoreboard writes
|
|
498
623
|
const hasTemp: Temp = `__opt_${stmt.name}_has`
|
|
@@ -549,9 +674,18 @@ function lowerStmt(
|
|
|
549
674
|
ctx.emit({ kind: 'copy', dst: valTemp, src: { kind: 'temp', name: '__rf_val' } })
|
|
550
675
|
const fieldTemps = new Map<string, Temp>([['has', hasTemp], ['val', valTemp]])
|
|
551
676
|
ctx.structVars.set(stmt.name, { typeName: '__option', fields: fieldTemps })
|
|
552
|
-
} else if (
|
|
677
|
+
} else if (
|
|
678
|
+
// Struct-typed let: explicit annotation OR inferred from @singleton::get() return
|
|
679
|
+
stmt.type?.kind === 'struct' ||
|
|
680
|
+
(stmt.init.kind === 'static_call' &&
|
|
681
|
+
ctx.singletonStructs.has((stmt.init as { kind: 'static_call'; type: string; method: string }).type) &&
|
|
682
|
+
(stmt.init as { kind: 'static_call'; method: string }).method === 'get')
|
|
683
|
+
) {
|
|
553
684
|
// Struct-typed let with non-literal init (e.g., call returning struct)
|
|
554
|
-
const
|
|
685
|
+
const inferredStructName = stmt.type?.kind === 'struct'
|
|
686
|
+
? stmt.type.name
|
|
687
|
+
: (stmt.init as { kind: 'static_call'; type: string }).type
|
|
688
|
+
const fields = ctx.structDefs.get(inferredStructName)
|
|
555
689
|
if (fields) {
|
|
556
690
|
lowerExpr(stmt.init, ctx, scope)
|
|
557
691
|
// Copy from return field slots into struct variable temps
|
|
@@ -567,7 +701,7 @@ function lowerStmt(
|
|
|
567
701
|
ctx.constTemps.set(t, constVal)
|
|
568
702
|
}
|
|
569
703
|
}
|
|
570
|
-
ctx.structVars.set(stmt.name, { typeName:
|
|
704
|
+
ctx.structVars.set(stmt.name, { typeName: inferredStructName, fields: fieldTemps })
|
|
571
705
|
} else {
|
|
572
706
|
const valOp = lowerExpr(stmt.init, ctx, scope)
|
|
573
707
|
const t = ctx.freshTemp()
|
|
@@ -701,6 +835,14 @@ function lowerStmt(
|
|
|
701
835
|
break
|
|
702
836
|
}
|
|
703
837
|
|
|
838
|
+
case 'const_decl': {
|
|
839
|
+
// Evaluate the literal at compile time and store in constValues for inlining at use sites
|
|
840
|
+
const op = lowerExpr(stmt.value, ctx, scope)
|
|
841
|
+
const numericValue = op.kind === 'const' ? op.value : 0
|
|
842
|
+
ctx.constValues.set(stmt.name, numericValue)
|
|
843
|
+
break
|
|
844
|
+
}
|
|
845
|
+
|
|
704
846
|
case 'expr': {
|
|
705
847
|
lowerExpr(stmt.expr, ctx, scope)
|
|
706
848
|
break
|
|
@@ -773,6 +915,33 @@ function lowerStmt(
|
|
|
773
915
|
break
|
|
774
916
|
}
|
|
775
917
|
|
|
918
|
+
case 'break_label': {
|
|
919
|
+
const loop = ctx.findLoopByLabel(stmt.label)
|
|
920
|
+
if (!loop) throw new Error(`break: label '${stmt.label}' not found`)
|
|
921
|
+
ctx.terminate({ kind: 'jump', target: loop.exit })
|
|
922
|
+
const dead = ctx.newBlock('post_break_label')
|
|
923
|
+
ctx.switchTo(dead)
|
|
924
|
+
break
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
case 'continue_label': {
|
|
928
|
+
const loop = ctx.findLoopByLabel(stmt.label)
|
|
929
|
+
if (!loop) throw new Error(`continue: label '${stmt.label}' not found`)
|
|
930
|
+
ctx.terminate({ kind: 'jump', target: loop.continueTo })
|
|
931
|
+
const dead = ctx.newBlock('post_continue_label')
|
|
932
|
+
ctx.switchTo(dead)
|
|
933
|
+
break
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
case 'labeled_loop': {
|
|
937
|
+
// The body is a while/foreach stmt; we need to push the label into the loop stack.
|
|
938
|
+
// We do this by injecting the label into the next pushLoop call by temporarily
|
|
939
|
+
// storing the pending label in ctx, then letting the inner loop case handle it.
|
|
940
|
+
ctx.setPendingLoopLabel(stmt.label)
|
|
941
|
+
lowerStmt(stmt.body, ctx, scope)
|
|
942
|
+
break
|
|
943
|
+
}
|
|
944
|
+
|
|
776
945
|
case 'if': {
|
|
777
946
|
const condOp = lowerExpr(stmt.cond, ctx, scope)
|
|
778
947
|
const thenBlock = ctx.newBlock('then')
|
|
@@ -876,6 +1045,8 @@ function lowerStmt(
|
|
|
876
1045
|
blocks: helperBlocks,
|
|
877
1046
|
entry: 'entry',
|
|
878
1047
|
isMacro: false,
|
|
1048
|
+
sourceLoc: stmt.span && ctx.sourceFile ? { file: ctx.sourceFile, line: stmt.span.line, col: stmt.span.col } : undefined,
|
|
1049
|
+
sourceSnippet: 'foreach helper',
|
|
879
1050
|
})
|
|
880
1051
|
|
|
881
1052
|
ctx.emit({ kind: 'call_context', fn: helperName, subcommands })
|
|
@@ -903,6 +1074,8 @@ function lowerStmt(
|
|
|
903
1074
|
blocks: execBlocks,
|
|
904
1075
|
entry: 'entry',
|
|
905
1076
|
isMacro: false,
|
|
1077
|
+
sourceLoc: stmt.span && ctx.sourceFile ? { file: ctx.sourceFile, line: stmt.span.line, col: stmt.span.col } : undefined,
|
|
1078
|
+
sourceSnippet: 'execute helper',
|
|
906
1079
|
})
|
|
907
1080
|
|
|
908
1081
|
ctx.emit({ kind: 'call_context', fn: helperName, subcommands })
|
|
@@ -910,6 +1083,53 @@ function lowerStmt(
|
|
|
910
1083
|
}
|
|
911
1084
|
|
|
912
1085
|
case 'match': {
|
|
1086
|
+
const hasStringPats = stmt.arms.some(a =>
|
|
1087
|
+
a.pattern.kind === 'PatExpr' && a.pattern.expr.kind === 'str_lit'
|
|
1088
|
+
)
|
|
1089
|
+
if (hasStringPats) {
|
|
1090
|
+
const matchPath = lowerStringExprToPath(stmt.expr, ctx, scope, 'match')
|
|
1091
|
+
if (!matchPath) {
|
|
1092
|
+
throw new Error('String match requires a string literal or tracked string variable')
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
const mergeBlock = ctx.newBlock('match_merge')
|
|
1096
|
+
for (let i = 0; i < stmt.arms.length; i++) {
|
|
1097
|
+
const arm = stmt.arms[i]
|
|
1098
|
+
const pat = arm.pattern
|
|
1099
|
+
|
|
1100
|
+
if (pat.kind === 'PatWild') {
|
|
1101
|
+
lowerBlock(arm.body, ctx, new Map(scope))
|
|
1102
|
+
if (isPlaceholderTerm(ctx.current().term)) {
|
|
1103
|
+
ctx.terminate({ kind: 'jump', target: mergeBlock.id })
|
|
1104
|
+
}
|
|
1105
|
+
continue
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
if (pat.kind === 'PatExpr' && pat.expr.kind === 'str_lit') {
|
|
1109
|
+
const cmpTemp = ctx.freshTemp()
|
|
1110
|
+
ctx.emit({ kind: 'string_match', dst: cmpTemp, ns: 'rs:strings', path: matchPath, value: pat.expr.value })
|
|
1111
|
+
const armBody = ctx.newBlock('match_arm')
|
|
1112
|
+
const nextArm = ctx.newBlock('match_next')
|
|
1113
|
+
ctx.terminate({ kind: 'branch', cond: { kind: 'temp', name: cmpTemp }, then: armBody.id, else: nextArm.id })
|
|
1114
|
+
ctx.switchTo(armBody)
|
|
1115
|
+
lowerBlock(arm.body, ctx, new Map(scope))
|
|
1116
|
+
if (isPlaceholderTerm(ctx.current().term)) {
|
|
1117
|
+
ctx.terminate({ kind: 'jump', target: mergeBlock.id })
|
|
1118
|
+
}
|
|
1119
|
+
ctx.switchTo(nextArm)
|
|
1120
|
+
continue
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
throw new Error(`Unsupported string match pattern: ${pat.kind}`)
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
if (isPlaceholderTerm(ctx.current().term)) {
|
|
1127
|
+
ctx.terminate({ kind: 'jump', target: mergeBlock.id })
|
|
1128
|
+
}
|
|
1129
|
+
ctx.switchTo(mergeBlock)
|
|
1130
|
+
break
|
|
1131
|
+
}
|
|
1132
|
+
|
|
913
1133
|
// Lower match as chained if/else
|
|
914
1134
|
const mergeBlock = ctx.newBlock('match_merge')
|
|
915
1135
|
|
|
@@ -1005,6 +1225,37 @@ function lowerStmt(
|
|
|
1005
1225
|
ctx.terminate({ kind: 'jump', target: mergeBlock.id })
|
|
1006
1226
|
}
|
|
1007
1227
|
ctx.switchTo(nextArm)
|
|
1228
|
+
} else if (pat.kind === 'PatEnum') {
|
|
1229
|
+
// Enum pattern: check tag value matches, then bind payload fields via NBT reads
|
|
1230
|
+
const tagValue = ctx.enumDefs.get(pat.enumName)?.get(pat.variant) ?? 0
|
|
1231
|
+
const cmpTemp = ctx.freshTemp()
|
|
1232
|
+
ctx.emit({ kind: 'cmp', dst: cmpTemp, op: 'eq', a: matchVal, b: { kind: 'const', value: tagValue } })
|
|
1233
|
+
const armBody = ctx.newBlock('match_arm')
|
|
1234
|
+
const nextArm = ctx.newBlock('match_next')
|
|
1235
|
+
ctx.terminate({ kind: 'branch', cond: { kind: 'temp', name: cmpTemp }, then: armBody.id, else: nextArm.id })
|
|
1236
|
+
ctx.switchTo(armBody)
|
|
1237
|
+
const armScope = new Map(scope)
|
|
1238
|
+
// Bind each pattern variable by reading the corresponding NBT payload field
|
|
1239
|
+
const payloadFields = ctx.enumPayloads.get(pat.enumName)?.get(pat.variant) ?? []
|
|
1240
|
+
for (let bi = 0; bi < pat.bindings.length; bi++) {
|
|
1241
|
+
const binding = pat.bindings[bi]
|
|
1242
|
+
const fieldDef = payloadFields[bi]
|
|
1243
|
+
const fieldName = fieldDef ? fieldDef.name : binding
|
|
1244
|
+
const bindTemp = ctx.freshTemp()
|
|
1245
|
+
ctx.emit({
|
|
1246
|
+
kind: 'nbt_read',
|
|
1247
|
+
dst: bindTemp,
|
|
1248
|
+
ns: 'rs:enums',
|
|
1249
|
+
path: `${pat.enumName}_${fieldName}`,
|
|
1250
|
+
scale: 1,
|
|
1251
|
+
})
|
|
1252
|
+
armScope.set(binding, bindTemp)
|
|
1253
|
+
}
|
|
1254
|
+
lowerBlock(arm.body, ctx, armScope)
|
|
1255
|
+
if (isPlaceholderTerm(ctx.current().term)) {
|
|
1256
|
+
ctx.terminate({ kind: 'jump', target: mergeBlock.id })
|
|
1257
|
+
}
|
|
1258
|
+
ctx.switchTo(nextArm)
|
|
1008
1259
|
} else if (pat.kind === 'PatExpr') {
|
|
1009
1260
|
// Legacy: range_lit or other expression
|
|
1010
1261
|
const expr = pat.expr
|
|
@@ -1126,6 +1377,58 @@ function lowerStmt(
|
|
|
1126
1377
|
break
|
|
1127
1378
|
}
|
|
1128
1379
|
|
|
1380
|
+
case 'while_let_some': {
|
|
1381
|
+
// while let Some(x) = init { body }
|
|
1382
|
+
// Compiles to: loop { check opt.has; if 0 break; bind x = opt.val; body }
|
|
1383
|
+
const headerBlock = ctx.newBlock('whl_header')
|
|
1384
|
+
const bodyBlock = ctx.newBlock('whl_body')
|
|
1385
|
+
const exitBlock = ctx.newBlock('whl_exit')
|
|
1386
|
+
|
|
1387
|
+
ctx.terminate({ kind: 'jump', target: headerBlock.id })
|
|
1388
|
+
|
|
1389
|
+
// Header: re-evaluate init and check has
|
|
1390
|
+
ctx.switchTo(headerBlock)
|
|
1391
|
+
|
|
1392
|
+
let hasOp: Operand
|
|
1393
|
+
let valTemp: Temp | undefined
|
|
1394
|
+
|
|
1395
|
+
const sv = (() => {
|
|
1396
|
+
if (stmt.init.kind === 'ident') return ctx.structVars.get(stmt.init.name)
|
|
1397
|
+
return undefined
|
|
1398
|
+
})()
|
|
1399
|
+
|
|
1400
|
+
if (sv && sv.typeName === '__option') {
|
|
1401
|
+
const hasT = sv.fields.get('has')!
|
|
1402
|
+
const valT = sv.fields.get('val')!
|
|
1403
|
+
hasOp = { kind: 'temp', name: hasT }
|
|
1404
|
+
valTemp = valT
|
|
1405
|
+
} else {
|
|
1406
|
+
lowerExpr(stmt.init, ctx, scope)
|
|
1407
|
+
const hasT = ctx.freshTemp()
|
|
1408
|
+
const valT = ctx.freshTemp()
|
|
1409
|
+
ctx.emit({ kind: 'copy', dst: hasT, src: { kind: 'temp', name: '__rf_has' } })
|
|
1410
|
+
ctx.emit({ kind: 'copy', dst: valT, src: { kind: 'temp', name: '__rf_val' } })
|
|
1411
|
+
hasOp = { kind: 'temp', name: hasT }
|
|
1412
|
+
valTemp = valT
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
ctx.terminate({ kind: 'branch', cond: hasOp, then: bodyBlock.id, else: exitBlock.id })
|
|
1416
|
+
|
|
1417
|
+
// Body: bind x = val, run body
|
|
1418
|
+
ctx.switchTo(bodyBlock)
|
|
1419
|
+
const bodyScope = new Map(scope)
|
|
1420
|
+
if (valTemp) bodyScope.set(stmt.binding, valTemp)
|
|
1421
|
+
ctx.pushLoop(headerBlock.id, exitBlock.id, headerBlock.id)
|
|
1422
|
+
lowerBlock(stmt.body, ctx, bodyScope)
|
|
1423
|
+
ctx.popLoop()
|
|
1424
|
+
if (isPlaceholderTerm(ctx.current().term)) {
|
|
1425
|
+
ctx.terminate({ kind: 'jump', target: headerBlock.id })
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
ctx.switchTo(exitBlock)
|
|
1429
|
+
break
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1129
1432
|
default: {
|
|
1130
1433
|
const _exhaustive: never = stmt
|
|
1131
1434
|
throw new Error(`Unknown HIR statement kind: ${(_exhaustive as any).kind}`)
|
|
@@ -1213,6 +1516,10 @@ function lowerExpr(
|
|
|
1213
1516
|
}
|
|
1214
1517
|
const temp = scope.get(expr.name)
|
|
1215
1518
|
if (temp) return { kind: 'temp', name: temp }
|
|
1519
|
+
// Module-level const: inline the literal value directly (no scoreboard slot)
|
|
1520
|
+
if (ctx.constValues.has(expr.name)) {
|
|
1521
|
+
return { kind: 'const', value: ctx.constValues.get(expr.name)! }
|
|
1522
|
+
}
|
|
1216
1523
|
// Unresolved ident — could be a global or external reference
|
|
1217
1524
|
const t = ctx.freshTemp()
|
|
1218
1525
|
ctx.emit({ kind: 'copy', dst: t, src: { kind: 'const', value: 0 } })
|
|
@@ -1346,6 +1653,36 @@ function lowerExpr(
|
|
|
1346
1653
|
return { kind: 'const', value }
|
|
1347
1654
|
}
|
|
1348
1655
|
|
|
1656
|
+
case 'enum_construct': {
|
|
1657
|
+
// Enum variant construction with payload:
|
|
1658
|
+
// Color::RGB(r: 10, g: 20, b: 30)
|
|
1659
|
+
// → scoreboard set tag = variant int value
|
|
1660
|
+
// → nbt_write rs:enums Color_r = 10, Color_g = 20, Color_b = 30
|
|
1661
|
+
const variants = ctx.enumDefs.get(expr.enumName)
|
|
1662
|
+
const tagValue = variants?.get(expr.variant) ?? 0
|
|
1663
|
+
// Write tag to a temp (the result of the expression is the integer tag)
|
|
1664
|
+
const tagTemp = ctx.freshTemp()
|
|
1665
|
+
ctx.emit({ kind: 'const', dst: tagTemp, value: tagValue })
|
|
1666
|
+
// Write payload fields to NBT storage rs:enums
|
|
1667
|
+
const payloadFields = ctx.enumPayloads.get(expr.enumName)?.get(expr.variant) ?? []
|
|
1668
|
+
for (const arg of expr.args) {
|
|
1669
|
+
const fieldDef = payloadFields.find(f => f.name === arg.name)
|
|
1670
|
+
const argOp = lowerExpr(arg.value, ctx, scope)
|
|
1671
|
+
// Determine NBT type from field definition
|
|
1672
|
+
const nbtType: NBTType = fieldDef && (fieldDef.type.kind === 'named') &&
|
|
1673
|
+
(fieldDef.type.name === 'float' || fieldDef.type.name === 'fixed') ? 'float' : 'int'
|
|
1674
|
+
ctx.emit({
|
|
1675
|
+
kind: 'nbt_write',
|
|
1676
|
+
ns: 'rs:enums',
|
|
1677
|
+
path: `${expr.enumName}_${arg.name}`,
|
|
1678
|
+
type: nbtType,
|
|
1679
|
+
scale: 1,
|
|
1680
|
+
src: argOp,
|
|
1681
|
+
})
|
|
1682
|
+
}
|
|
1683
|
+
return { kind: 'temp', name: tagTemp }
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1349
1686
|
case 'member': {
|
|
1350
1687
|
// Enum variant access via dot syntax: Phase.Idle → integer constant
|
|
1351
1688
|
if (expr.obj.kind === 'ident') {
|
|
@@ -1695,9 +2032,57 @@ function lowerExpr(
|
|
|
1695
2032
|
return { kind: 'temp', name: t }
|
|
1696
2033
|
}
|
|
1697
2034
|
|
|
2035
|
+
// Handle int_to_str / bool_to_str — identity functions for scoreboard int/bool → string
|
|
2036
|
+
// In f-string context these are handled by precomputeFStringParts; outside that, just
|
|
2037
|
+
// evaluate the argument and return it (integer stays as integer for scoreboard purposes).
|
|
2038
|
+
if (expr.fn === 'int_to_str' || expr.fn === 'bool_to_str') {
|
|
2039
|
+
if (expr.args.length === 1) {
|
|
2040
|
+
return lowerExpr(expr.args[0], ctx, scope)
|
|
2041
|
+
}
|
|
2042
|
+
const t = ctx.freshTemp()
|
|
2043
|
+
ctx.emit({ kind: 'const', dst: t, value: 0 })
|
|
2044
|
+
return { kind: 'temp', name: t }
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
// Handle assert(cond[, message]) — test framework builtin
|
|
2048
|
+
if (expr.fn === 'assert') {
|
|
2049
|
+
const condArg = expr.args[0]
|
|
2050
|
+
const msgArg = expr.args[1]
|
|
2051
|
+
const condOp = condArg ? lowerExpr(condArg, ctx, scope) : { kind: 'const' as const, value: 0 }
|
|
2052
|
+
// Evaluate condition to a temp
|
|
2053
|
+
let condTemp: string
|
|
2054
|
+
if (condOp.kind === 'temp') {
|
|
2055
|
+
condTemp = condOp.name
|
|
2056
|
+
} else {
|
|
2057
|
+
condTemp = ctx.freshTemp()
|
|
2058
|
+
ctx.emit({ kind: 'const', dst: condTemp, value: condOp.value })
|
|
2059
|
+
}
|
|
2060
|
+
// Get message string
|
|
2061
|
+
let msgStr = 'assert failed'
|
|
2062
|
+
if (msgArg && msgArg.kind === 'str_lit') {
|
|
2063
|
+
msgStr = msgArg.value
|
|
2064
|
+
}
|
|
2065
|
+
const obj = `__${ctx.getNamespace()}`
|
|
2066
|
+
// emit: execute unless score $condTemp <obj> matches 1 run tellraw @a "FAIL: <msg>"
|
|
2067
|
+
const failMsg = JSON.stringify({ text: `FAIL: ${msgStr}`, color: 'red' })
|
|
2068
|
+
ctx.emit({ kind: 'call', dst: null, fn: `__raw:execute unless score $${ctx.getFnName()}_${condTemp} ${obj} matches 1 run tellraw @a ${failMsg}`, args: [] })
|
|
2069
|
+
ctx.emit({ kind: 'call', dst: null, fn: `__raw:execute unless score $${ctx.getFnName()}_${condTemp} ${obj} matches 1 run scoreboard players add rs.test_failed rs.meta 1`, args: [] })
|
|
2070
|
+
const t = ctx.freshTemp()
|
|
2071
|
+
ctx.emit({ kind: 'const', dst: t, value: 0 })
|
|
2072
|
+
return { kind: 'temp', name: t }
|
|
2073
|
+
}
|
|
2074
|
+
|
|
1698
2075
|
// Handle builtin calls → raw MC commands
|
|
1699
2076
|
if (BUILTIN_SET.has(expr.fn)) {
|
|
1700
|
-
|
|
2077
|
+
// For text builtins with f-string args, precompute complex expressions to temp vars
|
|
2078
|
+
const TEXT_BUILTINS_SET = new Set(['tell', 'tellraw', 'title', 'subtitle', 'actionbar', 'announce'])
|
|
2079
|
+
let resolvedArgs = expr.args
|
|
2080
|
+
if (TEXT_BUILTINS_SET.has(expr.fn)) {
|
|
2081
|
+
resolvedArgs = expr.args.map(arg =>
|
|
2082
|
+
arg.kind === 'f_string' ? precomputeFStringParts(arg, ctx, scope) : arg
|
|
2083
|
+
)
|
|
2084
|
+
}
|
|
2085
|
+
const cmd = formatBuiltinCall(expr.fn, resolvedArgs, ctx.currentMacroParams, ctx.getNamespace())
|
|
1701
2086
|
ctx.emit({ kind: 'call', dst: null, fn: `__raw:${cmd}`, args: [] })
|
|
1702
2087
|
const t = ctx.freshTemp()
|
|
1703
2088
|
ctx.emit({ kind: 'const', dst: t, value: 0 })
|
|
@@ -1705,9 +2090,24 @@ function lowerExpr(
|
|
|
1705
2090
|
}
|
|
1706
2091
|
|
|
1707
2092
|
// Check for struct instance method call: parser desugars v.method() → call('method', [v, ...])
|
|
2093
|
+
// Some method names are remapped by the parser (e.g. add→set_add for set builtins).
|
|
2094
|
+
// We reverse-map them here to support impl methods with those names.
|
|
2095
|
+
const PARSER_METHOD_REMAP: Record<string, string> = {
|
|
2096
|
+
'set_add': 'add', 'set_contains': 'contains', 'set_remove': 'remove', 'set_clear': 'clear',
|
|
2097
|
+
'__array_push': 'push', '__array_pop': 'pop',
|
|
2098
|
+
'__entity_tag': 'tag', '__entity_untag': 'untag', '__entity_has_tag': 'has_tag',
|
|
2099
|
+
}
|
|
1708
2100
|
if (expr.args.length > 0 && expr.args[0].kind === 'ident') {
|
|
1709
2101
|
const sv = ctx.structVars.get(expr.args[0].name)
|
|
1710
2102
|
if (sv) {
|
|
2103
|
+
// Intercept Display::to_string() calls — inlined at f-string call sites; return 0 otherwise
|
|
2104
|
+
if (expr.fn === 'to_string' && ctx.displayImpls.has(sv.typeName)) {
|
|
2105
|
+
// Display::to_string() is expanded inline in f-string context (precomputeFStringParts).
|
|
2106
|
+
// Outside of that context, return a dummy 0 (the call is not valid standalone).
|
|
2107
|
+
const t = ctx.freshTemp()
|
|
2108
|
+
ctx.emit({ kind: 'const', dst: t, value: 0 })
|
|
2109
|
+
return { kind: 'temp', name: t }
|
|
2110
|
+
}
|
|
1711
2111
|
// Intercept Timer method calls when _id is a known compile-time constant
|
|
1712
2112
|
if (sv.typeName === 'Timer') {
|
|
1713
2113
|
const idTemp = sv.fields.get('_id')
|
|
@@ -1716,7 +2116,10 @@ function lowerExpr(
|
|
|
1716
2116
|
return lowerTimerMethod(expr.fn, timerId, sv, ctx, scope, expr.args.slice(1))
|
|
1717
2117
|
}
|
|
1718
2118
|
}
|
|
2119
|
+
// Try direct name, then try reverse-mapped name (for parser-remapped builtins)
|
|
2120
|
+
const originalMethodName = PARSER_METHOD_REMAP[expr.fn] ?? expr.fn
|
|
1719
2121
|
const methodInfo = ctx.implMethods.get(sv.typeName)?.get(expr.fn)
|
|
2122
|
+
?? ctx.implMethods.get(sv.typeName)?.get(originalMethodName)
|
|
1720
2123
|
if (methodInfo?.hasSelf) {
|
|
1721
2124
|
// Build args: self fields first, then remaining explicit args
|
|
1722
2125
|
const fields = ctx.structDefs.get(sv.typeName) ?? []
|
|
@@ -1745,7 +2148,7 @@ function lowerExpr(
|
|
|
1745
2148
|
}
|
|
1746
2149
|
const allArgs = [...selfArgs, ...explicitArgs]
|
|
1747
2150
|
const t = ctx.freshTemp()
|
|
1748
|
-
ctx.emit({ kind: 'call', dst: t, fn: `${sv.typeName}::${
|
|
2151
|
+
ctx.emit({ kind: 'call', dst: t, fn: `${sv.typeName}::${originalMethodName}`, args: allArgs })
|
|
1749
2152
|
return { kind: 'temp', name: t }
|
|
1750
2153
|
}
|
|
1751
2154
|
}
|
|
@@ -1754,21 +2157,45 @@ function lowerExpr(
|
|
|
1754
2157
|
// Check if calling a macro function → emit call_macro
|
|
1755
2158
|
const targetMacro = ctx.macroInfo.get(expr.fn)
|
|
1756
2159
|
if (targetMacro) {
|
|
1757
|
-
const args = expr.args.map(a => lowerExpr(a, ctx, scope))
|
|
1758
2160
|
const targetParams = ctx.fnParamInfo.get(expr.fn) ?? []
|
|
1759
2161
|
const macroArgs: { name: string; value: Operand; type: NBTType; scale: number }[] = []
|
|
1760
|
-
for (let i = 0; i < targetParams.length && i < args.length; i++) {
|
|
2162
|
+
for (let i = 0; i < targetParams.length && i < expr.args.length; i++) {
|
|
1761
2163
|
const paramName = targetParams[i].name
|
|
1762
2164
|
if (targetMacro.macroParams.has(paramName)) {
|
|
1763
2165
|
const paramTypeName = targetMacro.paramTypes.get(paramName) ?? 'int'
|
|
1764
|
-
const
|
|
1765
|
-
const
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
2166
|
+
const isString = paramTypeName === 'string' || paramTypeName === 'format_string'
|
|
2167
|
+
const isSelector = paramTypeName === 'selector'
|
|
2168
|
+
if (isString) {
|
|
2169
|
+
// String macro params: store directly to rs:macro_args as NBT string
|
|
2170
|
+
const srcPath = lowerStringExprToPath(expr.args[i], ctx, scope, paramName)
|
|
2171
|
+
if (srcPath) {
|
|
2172
|
+
ctx.emit({
|
|
2173
|
+
kind: 'call',
|
|
2174
|
+
dst: null,
|
|
2175
|
+
fn: `__raw:data modify storage rs:macro_args ${paramName} set from storage rs:strings ${srcPath}`,
|
|
2176
|
+
args: [],
|
|
2177
|
+
})
|
|
2178
|
+
}
|
|
2179
|
+
} else if (isSelector) {
|
|
2180
|
+
// Selector macro params: store the selector string as an NBT string in rs:macro_args
|
|
2181
|
+
const arg = expr.args[i]
|
|
2182
|
+
const selStr = arg.kind === 'selector' ? arg.raw : '@s'
|
|
2183
|
+
ctx.emit({
|
|
2184
|
+
kind: 'call',
|
|
2185
|
+
dst: null,
|
|
2186
|
+
fn: `__raw:data modify storage rs:macro_args ${paramName} set value ${JSON.stringify(selStr)}`,
|
|
2187
|
+
args: [],
|
|
2188
|
+
})
|
|
2189
|
+
} else {
|
|
2190
|
+
const isFloat = paramTypeName === 'float'
|
|
2191
|
+
const isFixed = paramTypeName === 'fixed'
|
|
2192
|
+
macroArgs.push({
|
|
2193
|
+
name: paramName,
|
|
2194
|
+
value: lowerExpr(expr.args[i], ctx, scope),
|
|
2195
|
+
type: (isFloat || isFixed) ? 'double' : 'int',
|
|
2196
|
+
scale: isFloat ? 0.01 : isFixed ? 0.0001 : 1,
|
|
2197
|
+
})
|
|
2198
|
+
}
|
|
1772
2199
|
}
|
|
1773
2200
|
}
|
|
1774
2201
|
const t = ctx.freshTemp()
|
|
@@ -1818,6 +2245,10 @@ function lowerExpr(
|
|
|
1818
2245
|
ctx.hirFunctions,
|
|
1819
2246
|
ctx.specializedFnsRegistry,
|
|
1820
2247
|
specializedName,
|
|
2248
|
+
ctx.enumPayloads,
|
|
2249
|
+
ctx.constValues,
|
|
2250
|
+
ctx.singletonStructs,
|
|
2251
|
+
ctx.displayImpls,
|
|
1821
2252
|
)
|
|
1822
2253
|
ctx.specializedFnsRegistry.set(specializedName, [specFn, ...specHelpers])
|
|
1823
2254
|
}
|
|
@@ -1842,6 +2273,36 @@ function lowerExpr(
|
|
|
1842
2273
|
{
|
|
1843
2274
|
const targetParams = ctx.fnParamInfo.get(expr.fn)
|
|
1844
2275
|
if (targetParams) {
|
|
2276
|
+
const hasStringParam = targetParams.some(
|
|
2277
|
+
p => p.type.kind === 'named' && (p.type.name === 'string' || p.type.name === 'format_string')
|
|
2278
|
+
)
|
|
2279
|
+
if (hasStringParam) {
|
|
2280
|
+
const nonStringArgs: Operand[] = []
|
|
2281
|
+
let stringSlot = 0
|
|
2282
|
+
for (let i = 0; i < targetParams.length && i < expr.args.length; i++) {
|
|
2283
|
+
const p = targetParams[i]
|
|
2284
|
+
if (p.type.kind === 'named' && (p.type.name === 'string' || p.type.name === 'format_string')) {
|
|
2285
|
+
const srcPath = lowerStringExprToPath(expr.args[i], ctx, scope, `arg${stringSlot}`)
|
|
2286
|
+
if (srcPath) {
|
|
2287
|
+
ctx.emit({
|
|
2288
|
+
kind: 'call',
|
|
2289
|
+
dst: null,
|
|
2290
|
+
fn: `__raw:data modify storage rs:strings __sp${stringSlot} set from storage rs:strings ${srcPath}`,
|
|
2291
|
+
args: [],
|
|
2292
|
+
})
|
|
2293
|
+
}
|
|
2294
|
+
stringSlot++
|
|
2295
|
+
} else {
|
|
2296
|
+
nonStringArgs.push(lowerExpr(expr.args[i], ctx, scope))
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
for (let i = targetParams.length; i < expr.args.length; i++) {
|
|
2300
|
+
nonStringArgs.push(lowerExpr(expr.args[i], ctx, scope))
|
|
2301
|
+
}
|
|
2302
|
+
const t = ctx.freshTemp()
|
|
2303
|
+
ctx.emit({ kind: 'call', dst: t, fn: expr.fn, args: nonStringArgs })
|
|
2304
|
+
return { kind: 'temp', name: t }
|
|
2305
|
+
}
|
|
1845
2306
|
const hasDoubleParam = targetParams.some(
|
|
1846
2307
|
p => p.type.kind === 'named' && p.type.name === 'double'
|
|
1847
2308
|
)
|
|
@@ -1914,6 +2375,12 @@ function lowerExpr(
|
|
|
1914
2375
|
if (expr.callee.kind === 'member' && expr.callee.obj.kind === 'ident') {
|
|
1915
2376
|
const sv = ctx.structVars.get(expr.callee.obj.name)
|
|
1916
2377
|
if (sv) {
|
|
2378
|
+
// Intercept Display::to_string() — inlined in f-string context; return 0 otherwise
|
|
2379
|
+
if (expr.callee.field === 'to_string' && ctx.displayImpls.has(sv.typeName)) {
|
|
2380
|
+
const t = ctx.freshTemp()
|
|
2381
|
+
ctx.emit({ kind: 'const', dst: t, value: 0 })
|
|
2382
|
+
return { kind: 'temp', name: t }
|
|
2383
|
+
}
|
|
1917
2384
|
// Intercept Timer method calls when _id is a known compile-time constant
|
|
1918
2385
|
if (sv.typeName === 'Timer') {
|
|
1919
2386
|
const idTemp = sv.fields.get('_id')
|
|
@@ -1938,6 +2405,35 @@ function lowerExpr(
|
|
|
1938
2405
|
}
|
|
1939
2406
|
}
|
|
1940
2407
|
}
|
|
2408
|
+
// Method chaining: callee obj is not a simple ident (e.g. v.scale(2).add(...))
|
|
2409
|
+
// Determine if the callee obj expression returns a struct via __rf_ slots
|
|
2410
|
+
if (expr.callee.kind === 'member' && expr.callee.obj.kind !== 'ident') {
|
|
2411
|
+
const returnedStructType = inferInvokeReturnStructType(expr.callee.obj, ctx)
|
|
2412
|
+
if (returnedStructType) {
|
|
2413
|
+
// Lower the inner call — result goes into __rf_ slots
|
|
2414
|
+
lowerExpr(expr.callee.obj, ctx, scope)
|
|
2415
|
+
// Read __rf_ slots into temps for this chained call
|
|
2416
|
+
const chainFields = ctx.structDefs.get(returnedStructType) ?? []
|
|
2417
|
+
const chainFieldTemps = new Map<string, Temp>()
|
|
2418
|
+
for (const fieldName of chainFields) {
|
|
2419
|
+
const ft = ctx.freshTemp()
|
|
2420
|
+
ctx.emit({ kind: 'copy', dst: ft, src: { kind: 'temp', name: `__rf_${fieldName}` } })
|
|
2421
|
+
chainFieldTemps.set(fieldName, ft)
|
|
2422
|
+
}
|
|
2423
|
+
const methodInfo = ctx.implMethods.get(returnedStructType)?.get(expr.callee.field)
|
|
2424
|
+
if (methodInfo?.hasSelf) {
|
|
2425
|
+
const selfArgs: Operand[] = chainFields.map(f => {
|
|
2426
|
+
const temp = chainFieldTemps.get(f)
|
|
2427
|
+
return temp ? { kind: 'temp' as const, name: temp } : { kind: 'const' as const, value: 0 }
|
|
2428
|
+
})
|
|
2429
|
+
const explicitArgs = expr.args.map(a => lowerExpr(a, ctx, scope))
|
|
2430
|
+
const allArgs = [...selfArgs, ...explicitArgs]
|
|
2431
|
+
const ct = ctx.freshTemp()
|
|
2432
|
+
ctx.emit({ kind: 'call', dst: ct, fn: `${returnedStructType}::${expr.callee.field}`, args: allArgs })
|
|
2433
|
+
return { kind: 'temp', name: ct }
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
1941
2437
|
// Fallback: generic invoke
|
|
1942
2438
|
const calleeOp = lowerExpr(expr.callee, ctx, scope)
|
|
1943
2439
|
const args = expr.args.map(a => lowerExpr(a, ctx, scope))
|
|
@@ -1965,6 +2461,28 @@ function lowerExpr(
|
|
|
1965
2461
|
ctx.emit({ kind: 'const', dst: t, value: 0 })
|
|
1966
2462
|
return { kind: 'temp', name: t }
|
|
1967
2463
|
}
|
|
2464
|
+
// @singleton struct static calls: expand struct arg field-by-field for ::set
|
|
2465
|
+
if (ctx.singletonStructs.has(expr.type)) {
|
|
2466
|
+
if (expr.method === 'get') {
|
|
2467
|
+
// GameState::get() — no struct args, our synthetic LIR fn writes to $__rf_<field> slots
|
|
2468
|
+
const t = ctx.freshTemp()
|
|
2469
|
+
ctx.emit({ kind: 'call', dst: t, fn: `${expr.type}::${expr.method}`, args: [] })
|
|
2470
|
+
return { kind: 'temp', name: t }
|
|
2471
|
+
} else if (expr.method === 'set' && expr.args.length === 1 && expr.args[0].kind === 'ident') {
|
|
2472
|
+
// GameState::set(gs) — flatten struct arg into individual field args ($p0, $p1, ...)
|
|
2473
|
+
const sv = ctx.structVars.get(expr.args[0].name)
|
|
2474
|
+
if (sv) {
|
|
2475
|
+
const fields = ctx.structDefs.get(sv.typeName) ?? []
|
|
2476
|
+
const fieldArgs: Operand[] = fields.map(f => {
|
|
2477
|
+
const temp = sv.fields.get(f)
|
|
2478
|
+
return temp ? { kind: 'temp' as const, name: temp } : { kind: 'const' as const, value: 0 }
|
|
2479
|
+
})
|
|
2480
|
+
const t = ctx.freshTemp()
|
|
2481
|
+
ctx.emit({ kind: 'call', dst: t, fn: `${expr.type}::${expr.method}`, args: fieldArgs })
|
|
2482
|
+
return { kind: 'temp', name: t }
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
1968
2486
|
const args = expr.args.map(a => lowerExpr(a, ctx, scope))
|
|
1969
2487
|
const t = ctx.freshTemp()
|
|
1970
2488
|
ctx.emit({ kind: 'call', dst: t, fn: `${expr.type}::${expr.method}`, args })
|
|
@@ -2002,6 +2520,47 @@ function lowerExpr(
|
|
|
2002
2520
|
return { kind: 'temp', name: t }
|
|
2003
2521
|
}
|
|
2004
2522
|
|
|
2523
|
+
case 'unwrap_or': {
|
|
2524
|
+
// opt.unwrap_or(default) → evaluate opt, if has=1 return val else return default
|
|
2525
|
+
const resultTemp = ctx.freshTemp()
|
|
2526
|
+
const defaultOp = lowerExpr(expr.default_, ctx, scope)
|
|
2527
|
+
ctx.emit({ kind: 'copy', dst: resultTemp, src: defaultOp })
|
|
2528
|
+
|
|
2529
|
+
const sv = (() => {
|
|
2530
|
+
if (expr.opt.kind === 'ident') return ctx.structVars.get(expr.opt.name)
|
|
2531
|
+
return undefined
|
|
2532
|
+
})()
|
|
2533
|
+
|
|
2534
|
+
let hasOp: Operand
|
|
2535
|
+
let valTemp: Temp | undefined
|
|
2536
|
+
|
|
2537
|
+
if (sv && sv.typeName === '__option') {
|
|
2538
|
+
const hasT = sv.fields.get('has')!
|
|
2539
|
+
const valT = sv.fields.get('val')!
|
|
2540
|
+
hasOp = { kind: 'temp', name: hasT }
|
|
2541
|
+
valTemp = valT
|
|
2542
|
+
} else {
|
|
2543
|
+
lowerExpr(expr.opt, ctx, scope)
|
|
2544
|
+
const hasT = ctx.freshTemp()
|
|
2545
|
+
const valT = ctx.freshTemp()
|
|
2546
|
+
ctx.emit({ kind: 'copy', dst: hasT, src: { kind: 'temp', name: '__rf_has' } })
|
|
2547
|
+
ctx.emit({ kind: 'copy', dst: valT, src: { kind: 'temp', name: '__rf_val' } })
|
|
2548
|
+
hasOp = { kind: 'temp', name: hasT }
|
|
2549
|
+
valTemp = valT
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2552
|
+
const someBlock = ctx.newBlock('unwrap_some')
|
|
2553
|
+
const mergeBlock = ctx.newBlock('unwrap_merge')
|
|
2554
|
+
ctx.terminate({ kind: 'branch', cond: hasOp, then: someBlock.id, else: mergeBlock.id })
|
|
2555
|
+
|
|
2556
|
+
ctx.switchTo(someBlock)
|
|
2557
|
+
if (valTemp) ctx.emit({ kind: 'copy', dst: resultTemp, src: { kind: 'temp', name: valTemp } })
|
|
2558
|
+
ctx.terminate({ kind: 'jump', target: mergeBlock.id })
|
|
2559
|
+
|
|
2560
|
+
ctx.switchTo(mergeBlock)
|
|
2561
|
+
return { kind: 'temp', name: resultTemp }
|
|
2562
|
+
}
|
|
2563
|
+
|
|
2005
2564
|
case 'type_cast': {
|
|
2006
2565
|
const ns = ctx.getNamespace()
|
|
2007
2566
|
const targetName = expr.targetType.kind === 'named' ? expr.targetType.name : null
|
|
@@ -2157,6 +2716,34 @@ function lowerShortCircuitOr(
|
|
|
2157
2716
|
// Timer method inlining
|
|
2158
2717
|
// ---------------------------------------------------------------------------
|
|
2159
2718
|
|
|
2719
|
+
/**
|
|
2720
|
+
* Infer the struct type name returned by a chained invoke/call expression.
|
|
2721
|
+
* Used to support method chaining: v.scale(2).add(...) where scale() returns Vec2.
|
|
2722
|
+
* Returns the struct type name if determinable, otherwise undefined.
|
|
2723
|
+
*/
|
|
2724
|
+
function inferInvokeReturnStructType(
|
|
2725
|
+
expr: HIRExpr,
|
|
2726
|
+
ctx: FnContext,
|
|
2727
|
+
): string | undefined {
|
|
2728
|
+
if (expr.kind === 'invoke' && expr.callee.kind === 'member') {
|
|
2729
|
+
// Find the receiver type via structVars
|
|
2730
|
+
let receiverTypeName: string | undefined
|
|
2731
|
+
if (expr.callee.obj.kind === 'ident') {
|
|
2732
|
+
receiverTypeName = ctx.structVars.get(expr.callee.obj.name)?.typeName
|
|
2733
|
+
} else {
|
|
2734
|
+
// Recursively infer the type for deeper chains
|
|
2735
|
+
receiverTypeName = inferInvokeReturnStructType(expr.callee.obj, ctx)
|
|
2736
|
+
}
|
|
2737
|
+
if (receiverTypeName) {
|
|
2738
|
+
const methodInfo = ctx.implMethods.get(receiverTypeName)?.get(expr.callee.field)
|
|
2739
|
+
if (methodInfo?.returnStructName) {
|
|
2740
|
+
return methodInfo.returnStructName
|
|
2741
|
+
}
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
return undefined
|
|
2745
|
+
}
|
|
2746
|
+
|
|
2160
2747
|
/**
|
|
2161
2748
|
* Inline a Timer instance method call using the statically-assigned timer ID.
|
|
2162
2749
|
* Emits scoreboard operations directly, bypassing the Timer::* function calls.
|
|
@@ -2296,6 +2883,43 @@ function lowerExecuteSubcmd(sub: HIRExecuteSubcommand): ExecuteSubcmd {
|
|
|
2296
2883
|
}
|
|
2297
2884
|
}
|
|
2298
2885
|
|
|
2886
|
+
function lowerStringExprToPath(
|
|
2887
|
+
expr: HIRExpr,
|
|
2888
|
+
ctx: FnContext,
|
|
2889
|
+
scope: Map<string, Temp>,
|
|
2890
|
+
hint = 'str',
|
|
2891
|
+
): string | null {
|
|
2892
|
+
switch (expr.kind) {
|
|
2893
|
+
case 'str_lit': {
|
|
2894
|
+
const path = ctx.freshStringVar(hint)
|
|
2895
|
+
ctx.emit({
|
|
2896
|
+
kind: 'call',
|
|
2897
|
+
dst: null,
|
|
2898
|
+
fn: `__raw:data modify storage rs:strings ${path} set value ${JSON.stringify(expr.value)}`,
|
|
2899
|
+
args: [],
|
|
2900
|
+
})
|
|
2901
|
+
return path
|
|
2902
|
+
}
|
|
2903
|
+
case 'ident':
|
|
2904
|
+
return ctx.stringVars.get(expr.name) ?? null
|
|
2905
|
+
case 'assign': {
|
|
2906
|
+
if (!ctx.stringVars.has(expr.target)) return null
|
|
2907
|
+
const dstPath = ctx.stringVars.get(expr.target)!
|
|
2908
|
+
const srcPath = lowerStringExprToPath(expr.value, ctx, scope, expr.target)
|
|
2909
|
+
if (!srcPath || srcPath === dstPath) return dstPath
|
|
2910
|
+
ctx.emit({
|
|
2911
|
+
kind: 'call',
|
|
2912
|
+
dst: null,
|
|
2913
|
+
fn: `__raw:data modify storage rs:strings ${dstPath} set from storage rs:strings ${srcPath}`,
|
|
2914
|
+
args: [],
|
|
2915
|
+
})
|
|
2916
|
+
return dstPath
|
|
2917
|
+
}
|
|
2918
|
+
default:
|
|
2919
|
+
return null
|
|
2920
|
+
}
|
|
2921
|
+
}
|
|
2922
|
+
|
|
2299
2923
|
function selectorToString(sel: { kind: string; filters?: any }): string {
|
|
2300
2924
|
// EntitySelector has kind like '@a', '@e', '@s', etc.
|
|
2301
2925
|
// Filters are key=value pairs that become [key=value,key=value]
|
|
@@ -2336,6 +2960,105 @@ const MACRO_SENTINEL = '\x01'
|
|
|
2336
2960
|
* Convert an f_string HIRExpr to a Minecraft JSON text component string.
|
|
2337
2961
|
* Each interpolated variable becomes a {"score":{"name":"$var","objective":"__ns"}} component.
|
|
2338
2962
|
*/
|
|
2963
|
+
/**
|
|
2964
|
+
* Pre-evaluate complex f-string expression parts to MIR temp vars,
|
|
2965
|
+
* returning a rewritten HIRExpr where each complex part is replaced by a simple ident.
|
|
2966
|
+
*/
|
|
2967
|
+
function precomputeFStringParts(
|
|
2968
|
+
expr: HIRExpr,
|
|
2969
|
+
ctx: FnContext,
|
|
2970
|
+
scope: Map<string, Temp>,
|
|
2971
|
+
): HIRExpr {
|
|
2972
|
+
if (expr.kind !== 'f_string') return expr
|
|
2973
|
+
|
|
2974
|
+
const newParts: HIRFStringPart[] = []
|
|
2975
|
+
|
|
2976
|
+
for (const part of expr.parts) {
|
|
2977
|
+
if (part.kind === 'text') {
|
|
2978
|
+
newParts.push(part)
|
|
2979
|
+
continue
|
|
2980
|
+
}
|
|
2981
|
+
|
|
2982
|
+
const inner: HIRExpr = part.expr
|
|
2983
|
+
// Simple cases that fStringToJsonText already handles
|
|
2984
|
+
if (inner.kind === 'ident' || inner.kind === 'int_lit' || inner.kind === 'bool_lit') {
|
|
2985
|
+
newParts.push({ kind: 'expr', expr: inner })
|
|
2986
|
+
continue
|
|
2987
|
+
}
|
|
2988
|
+
|
|
2989
|
+
// Display::to_string() inline expansion in f-string context:
|
|
2990
|
+
// v.to_string() (desugared as call{fn:'to_string', args:[v]}) → inline the Display impl's f-string
|
|
2991
|
+
if (inner.kind === 'call' && inner.fn === 'to_string' && inner.args.length === 1 && inner.args[0].kind === 'ident') {
|
|
2992
|
+
const argName = (inner.args[0] as { kind: 'ident'; name: string }).name
|
|
2993
|
+
const sv = ctx.structVars.get(argName)
|
|
2994
|
+
if (sv && ctx.displayImpls.has(sv.typeName)) {
|
|
2995
|
+
const displayParts = ctx.displayImpls.get(sv.typeName)!
|
|
2996
|
+
// Expand Display impl parts, substituting self.<field> with the actual struct field temps
|
|
2997
|
+
for (const dp of displayParts) {
|
|
2998
|
+
if (dp.kind === 'text') {
|
|
2999
|
+
newParts.push(dp)
|
|
3000
|
+
} else {
|
|
3001
|
+
const dpExpr = dp.expr
|
|
3002
|
+
// member(ident('self'), field) → lookup struct field temp
|
|
3003
|
+
if (dpExpr.kind === 'member' && dpExpr.obj.kind === 'ident' && dpExpr.obj.name === 'self') {
|
|
3004
|
+
const fieldTemp = sv.fields.get(dpExpr.field)
|
|
3005
|
+
if (fieldTemp) {
|
|
3006
|
+
newParts.push({ kind: 'expr', expr: { kind: 'ident', name: fieldTemp } })
|
|
3007
|
+
} else {
|
|
3008
|
+
newParts.push({ kind: 'expr', expr: { kind: 'int_lit', value: 0 } })
|
|
3009
|
+
}
|
|
3010
|
+
} else {
|
|
3011
|
+
// Other expressions: lower them with self fields in scope
|
|
3012
|
+
const selfScope = new Map(scope)
|
|
3013
|
+
for (const [fieldName, fieldTemp] of sv.fields) {
|
|
3014
|
+
selfScope.set(`self.${fieldName}`, fieldTemp)
|
|
3015
|
+
}
|
|
3016
|
+
const tempOp = lowerExpr(dpExpr, ctx, selfScope)
|
|
3017
|
+
if (tempOp.kind === 'temp') {
|
|
3018
|
+
newParts.push({ kind: 'expr', expr: { kind: 'ident', name: tempOp.name } })
|
|
3019
|
+
} else if (tempOp.kind === 'const') {
|
|
3020
|
+
newParts.push({ kind: 'expr', expr: { kind: 'int_lit', value: tempOp.value } })
|
|
3021
|
+
}
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
}
|
|
3025
|
+
continue
|
|
3026
|
+
}
|
|
3027
|
+
}
|
|
3028
|
+
|
|
3029
|
+
// int_to_str(x) / bool_to_str(x) in f-string: pass through the inner arg as a scoreboard score ref
|
|
3030
|
+
if (inner.kind === 'call' && (inner.fn === 'int_to_str' || inner.fn === 'bool_to_str') && inner.args.length === 1) {
|
|
3031
|
+
const arg: HIRExpr = inner.args[0]
|
|
3032
|
+
if (arg.kind === 'ident') {
|
|
3033
|
+
newParts.push({ kind: 'expr', expr: arg })
|
|
3034
|
+
continue
|
|
3035
|
+
}
|
|
3036
|
+
// If arg is complex, lower it first
|
|
3037
|
+
const argOp = lowerExpr(arg, ctx, scope)
|
|
3038
|
+
if (argOp.kind === 'temp') {
|
|
3039
|
+
newParts.push({ kind: 'expr', expr: { kind: 'ident', name: argOp.name } })
|
|
3040
|
+
} else if (argOp.kind === 'const') {
|
|
3041
|
+
newParts.push({ kind: 'expr', expr: { kind: 'int_lit', value: argOp.value } })
|
|
3042
|
+
} else {
|
|
3043
|
+
newParts.push(part)
|
|
3044
|
+
}
|
|
3045
|
+
continue
|
|
3046
|
+
}
|
|
3047
|
+
|
|
3048
|
+
// Complex expression: lower to a temp var, then reference it as ident
|
|
3049
|
+
const tempOp = lowerExpr(inner, ctx, scope)
|
|
3050
|
+
if (tempOp.kind === 'temp') {
|
|
3051
|
+
newParts.push({ kind: 'expr', expr: { kind: 'ident', name: tempOp.name } })
|
|
3052
|
+
} else if (tempOp.kind === 'const') {
|
|
3053
|
+
newParts.push({ kind: 'expr', expr: { kind: 'int_lit', value: tempOp.value } })
|
|
3054
|
+
} else {
|
|
3055
|
+
newParts.push(part)
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
|
|
3059
|
+
return { kind: 'f_string', parts: newParts }
|
|
3060
|
+
}
|
|
3061
|
+
|
|
2339
3062
|
function fStringToJsonText(expr: HIRExpr, namespace: string): string {
|
|
2340
3063
|
if (expr.kind !== 'f_string') return JSON.stringify(expr.kind === 'str_lit' ? { text: expr.value } : { text: '~' })
|
|
2341
3064
|
const objective = `__${namespace}`
|
|
@@ -2344,12 +3067,14 @@ function fStringToJsonText(expr: HIRExpr, namespace: string): string {
|
|
|
2344
3067
|
if (part.kind === 'text') {
|
|
2345
3068
|
if (part.value) extra.push({ text: part.value })
|
|
2346
3069
|
} else {
|
|
2347
|
-
// expr part — must be a scoreboard variable (ident)
|
|
2348
|
-
const inner = part.expr
|
|
3070
|
+
// expr part — must be a scoreboard variable (ident) or literal
|
|
3071
|
+
const inner = part.expr as HIRExpr
|
|
2349
3072
|
if (inner.kind === 'ident') {
|
|
2350
3073
|
extra.push({ score: { name: `$${inner.name}`, objective } })
|
|
2351
3074
|
} else if (inner.kind === 'int_lit') {
|
|
2352
3075
|
extra.push({ text: String(inner.value) })
|
|
3076
|
+
} else if (inner.kind === 'bool_lit') {
|
|
3077
|
+
extra.push({ text: inner.value ? 'true' : 'false' })
|
|
2353
3078
|
} else {
|
|
2354
3079
|
extra.push({ text: '?' })
|
|
2355
3080
|
}
|
|
@@ -2473,6 +3198,26 @@ function formatBuiltinCall(
|
|
|
2473
3198
|
case 'difficulty': cmd = `difficulty ${strs[0]}`; break
|
|
2474
3199
|
case 'xp_add': cmd = `xp add ${strs[0]} ${strs[1]} ${strs[2] ?? 'points'}`; break
|
|
2475
3200
|
case 'xp_set': cmd = `xp set ${strs[0]} ${strs[1]} ${strs[2] ?? 'points'}`; break
|
|
3201
|
+
case 'scoreboard_add_objective': cmd = strs[2] ? `scoreboard objectives add ${strs[0]} ${strs[1]} ${strs[2]}` : `scoreboard objectives add ${strs[0]} ${strs[1]}`; break
|
|
3202
|
+
case 'scoreboard_remove_objective': cmd = `scoreboard objectives remove ${strs[0]}`; break
|
|
3203
|
+
case 'scoreboard_display': cmd = `scoreboard objectives setdisplay ${strs[0]} ${strs[1] ?? ''}`; break
|
|
3204
|
+
case 'scoreboard_hide': cmd = `scoreboard objectives setdisplay ${strs[0]}`; break
|
|
3205
|
+
case 'team_add': cmd = strs[1] ? `team add ${strs[0]} ${strs[1]}` : `team add ${strs[0]}`; break
|
|
3206
|
+
case 'team_remove': cmd = `team remove ${strs[0]}`; break
|
|
3207
|
+
case 'team_join': cmd = `team join ${strs[0]} ${strs[1]}`; break
|
|
3208
|
+
case 'team_leave': cmd = `team leave ${strs[0]}`; break
|
|
3209
|
+
case 'team_option': cmd = `team modify ${strs[0]} ${strs[1]} ${strs[2]}`; break
|
|
3210
|
+
case 'bossbar_add': cmd = `bossbar add ${strs[0]} ${strs[1] ? JSON.stringify({ text: strs[1] }) : '""'}`; break
|
|
3211
|
+
case 'bossbar_remove': cmd = `bossbar remove ${strs[0]}`; break
|
|
3212
|
+
case 'bossbar_set_value': cmd = `bossbar set ${strs[0]} value ${strs[1]}`; break
|
|
3213
|
+
case 'bossbar_get_value': cmd = `bossbar get ${strs[0]} value`; break
|
|
3214
|
+
case 'bossbar_set_max': cmd = `bossbar set ${strs[0]} max ${strs[1]}`; break
|
|
3215
|
+
case 'bossbar_set_color': cmd = `bossbar set ${strs[0]} color ${strs[1]}`; break
|
|
3216
|
+
case 'bossbar_set_style': cmd = `bossbar set ${strs[0]} style ${strs[1]}`; break
|
|
3217
|
+
case 'bossbar_set_visible': cmd = `bossbar set ${strs[0]} visible ${strs[1]}`; break
|
|
3218
|
+
case 'bossbar_set_players': cmd = `bossbar set ${strs[0]} players ${strs[1]}`; break
|
|
3219
|
+
case 'data_get': cmd = `data get ${strs[0]} ${strs[1]} ${strs[2] ?? ''}`.trimEnd(); break
|
|
3220
|
+
case 'data_merge': cmd = `data merge ${strs[0]} ${strs[1]} ${strs[2]}`; break
|
|
2476
3221
|
default: cmd = `${fn} ${strs.join(' ')}`
|
|
2477
3222
|
}
|
|
2478
3223
|
|