@typecad/cuttlefish 0.1.0-alpha.1
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/LICENSE +21 -0
- package/README.md +79 -0
- package/dist/api/board-types.d.ts +1 -0
- package/dist/api/board-types.js +4 -0
- package/dist/api/capabilities.d.ts +12 -0
- package/dist/api/capabilities.js +1 -0
- package/dist/api/config.d.ts +120 -0
- package/dist/api/config.js +7 -0
- package/dist/api/index.d.ts +5 -0
- package/dist/api/index.js +2 -0
- package/dist/api/schema/builder.d.ts +14 -0
- package/dist/api/schema/builder.js +15 -0
- package/dist/api/schema/index.d.ts +2 -0
- package/dist/api/schema/index.js +1 -0
- package/dist/api/schema/types.d.ts +257 -0
- package/dist/api/schema/types.js +4 -0
- package/dist/api/shared/async-runtime-static.d.ts +8 -0
- package/dist/api/shared/async-runtime-static.js +201 -0
- package/dist/api/shared/async-types.d.ts +42 -0
- package/dist/api/shared/async-types.js +8 -0
- package/dist/api/shared/board-resolver.d.ts +12 -0
- package/dist/api/shared/board-resolver.js +7 -0
- package/dist/api/shared/cpp-type-ir.d.ts +229 -0
- package/dist/api/shared/cpp-type-ir.js +672 -0
- package/dist/api/shared/display-adapter.d.ts +12 -0
- package/dist/api/shared/display-adapter.js +145 -0
- package/dist/api/shared/display-adapters/eink-mono.d.ts +2 -0
- package/dist/api/shared/display-adapters/eink-mono.js +53 -0
- package/dist/api/shared/display-adapters/sdl.d.ts +2 -0
- package/dist/api/shared/display-adapters/sdl.js +452 -0
- package/dist/api/shared/display-adapters/ssd1309.d.ts +2 -0
- package/dist/api/shared/display-adapters/ssd1309.js +136 -0
- package/dist/api/shared/display-adapters/st7796.d.ts +2 -0
- package/dist/api/shared/display-adapters/st7796.js +132 -0
- package/dist/api/shared/display-capabilities.d.ts +45 -0
- package/dist/api/shared/display-capabilities.js +54 -0
- package/dist/api/shared/display-op-ir.d.ts +62 -0
- package/dist/api/shared/display-op-ir.js +19 -0
- package/dist/api/shared/display-profile.d.ts +265 -0
- package/dist/api/shared/display-profile.js +338 -0
- package/dist/api/shared/graphics-strategy.d.ts +29 -0
- package/dist/api/shared/graphics-strategy.js +11 -0
- package/dist/api/shared/hal-op-ir.d.ts +393 -0
- package/dist/api/shared/hal-op-ir.js +18 -0
- package/dist/api/shared/index.d.ts +26 -0
- package/dist/api/shared/index.js +21 -0
- package/dist/api/shared/ir-core.d.ts +363 -0
- package/dist/api/shared/ir-core.js +8 -0
- package/dist/api/shared/ir-declarations.d.ts +201 -0
- package/dist/api/shared/ir-declarations.js +26 -0
- package/dist/api/shared/ir.d.ts +85 -0
- package/dist/api/shared/ir.js +13 -0
- package/dist/api/shared/platform-strategy.d.ts +402 -0
- package/dist/api/shared/platform-strategy.js +14 -0
- package/dist/api/shared/polyfill-helper-registry.d.ts +11 -0
- package/dist/api/shared/polyfill-helper-registry.js +86 -0
- package/dist/api/shared/polyfill-types.d.ts +33 -0
- package/dist/api/shared/polyfill-types.js +76 -0
- package/dist/api/shared/promise-runtime.d.ts +10 -0
- package/dist/api/shared/promise-runtime.js +244 -0
- package/dist/api/shared/snprintf-types.d.ts +29 -0
- package/dist/api/shared/snprintf-types.js +8 -0
- package/dist/api/shared/string-method-registry.d.ts +53 -0
- package/dist/api/shared/string-method-registry.js +168 -0
- package/dist/api/shared/toolchain-types.d.ts +63 -0
- package/dist/api/shared/toolchain-types.js +71 -0
- package/dist/api/shared/types.d.ts +50 -0
- package/dist/api/shared/types.js +6 -0
- package/dist/ast/parse.d.ts +2 -0
- package/dist/ast/parse.js +4 -0
- package/dist/cache.d.ts +26 -0
- package/dist/cache.js +95 -0
- package/dist/cli-utils.d.ts +37 -0
- package/dist/cli-utils.js +250 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +635 -0
- package/dist/config-loader.d.ts +78 -0
- package/dist/config-loader.js +508 -0
- package/dist/config-schema.d.ts +233 -0
- package/dist/config-schema.js +83 -0
- package/dist/create/board-checklist.d.ts +2 -0
- package/dist/create/board-checklist.js +52 -0
- package/dist/create/board-codegen.d.ts +9 -0
- package/dist/create/board-codegen.js +238 -0
- package/dist/create/board-generators.d.ts +12 -0
- package/dist/create/board-generators.js +652 -0
- package/dist/create/board-spec.d.ts +640 -0
- package/dist/create/board-spec.js +215 -0
- package/dist/create/eslint-rules-template.d.ts +2 -0
- package/dist/create/eslint-rules-template.js +672 -0
- package/dist/create/index.d.ts +11 -0
- package/dist/create/index.js +8 -0
- package/dist/create/init-scaffold.d.ts +27 -0
- package/dist/create/init-scaffold.js +150 -0
- package/dist/create/init-templates.d.ts +24 -0
- package/dist/create/init-templates.js +379 -0
- package/dist/create/init-wizard.d.ts +8 -0
- package/dist/create/init-wizard.js +168 -0
- package/dist/debug/breakpoint-loader.d.ts +24 -0
- package/dist/debug/breakpoint-loader.js +137 -0
- package/dist/debug/index.d.ts +3 -0
- package/dist/debug/index.js +7 -0
- package/dist/debug/preprocessor.d.ts +9 -0
- package/dist/debug/preprocessor.js +214 -0
- package/dist/debug/types.d.ts +32 -0
- package/dist/debug/types.js +6 -0
- package/dist/diagnostics/diagnostics-report.d.ts +51 -0
- package/dist/diagnostics/diagnostics-report.js +333 -0
- package/dist/diagnostics/json-schema.d.ts +138 -0
- package/dist/diagnostics/json-schema.js +27 -0
- package/dist/diagnostics/md-writer.d.ts +10 -0
- package/dist/diagnostics/md-writer.js +336 -0
- package/dist/diagnostics/mermaid-builder.d.ts +65 -0
- package/dist/diagnostics/mermaid-builder.js +681 -0
- package/dist/emit/cpp-emitter.d.ts +6 -0
- package/dist/emit/cpp-emitter.js +48 -0
- package/dist/emit/emitters/class-emitter.d.ts +2 -0
- package/dist/emit/emitters/class-emitter.js +523 -0
- package/dist/emit/emitters/emitter-context.d.ts +174 -0
- package/dist/emit/emitters/emitter-context.js +1 -0
- package/dist/emit/emitters/entrypoint-synthesizer.d.ts +2 -0
- package/dist/emit/emitters/entrypoint-synthesizer.js +175 -0
- package/dist/emit/emitters/function-emitter-impl.d.ts +19 -0
- package/dist/emit/emitters/function-emitter-impl.js +428 -0
- package/dist/emit/emitters/line-appender.d.ts +15 -0
- package/dist/emit/emitters/line-appender.js +344 -0
- package/dist/emit/emitters/namespace-emitter.d.ts +2 -0
- package/dist/emit/emitters/namespace-emitter.js +324 -0
- package/dist/emit/emitters/output-finalizer.d.ts +4 -0
- package/dist/emit/emitters/output-finalizer.js +267 -0
- package/dist/emit/emitters/setup.d.ts +3 -0
- package/dist/emit/emitters/setup.js +851 -0
- package/dist/emit/emitters/top-level-prep.d.ts +2 -0
- package/dist/emit/emitters/top-level-prep.js +706 -0
- package/dist/emit/emitters/type-decl-emitter.d.ts +2 -0
- package/dist/emit/emitters/type-decl-emitter.js +251 -0
- package/dist/emit/emitters/ui-emitter.d.ts +21 -0
- package/dist/emit/emitters/ui-emitter.js +564 -0
- package/dist/emit/enum-emitter.d.ts +1 -0
- package/dist/emit/enum-emitter.js +1 -0
- package/dist/emit/expression-renderer.d.ts +283 -0
- package/dist/emit/expression-renderer.js +1477 -0
- package/dist/emit/native-helpers-emitter.d.ts +11 -0
- package/dist/emit/native-helpers-emitter.js +34 -0
- package/dist/emit/route-hal-op.d.ts +5 -0
- package/dist/emit/route-hal-op.js +13 -0
- package/dist/emit/snprintf-helpers.d.ts +17 -0
- package/dist/emit/snprintf-helpers.js +462 -0
- package/dist/emit/statement-renderer.d.ts +189 -0
- package/dist/emit/statement-renderer.js +948 -0
- package/dist/emit/utils/async-state-machine.d.ts +42 -0
- package/dist/emit/utils/async-state-machine.js +343 -0
- package/dist/emit/utils/comment-helpers.d.ts +20 -0
- package/dist/emit/utils/comment-helpers.js +31 -0
- package/dist/emit/utils/cpp-helpers.d.ts +47 -0
- package/dist/emit/utils/cpp-helpers.js +85 -0
- package/dist/emit/utils/include-resolver.d.ts +58 -0
- package/dist/emit/utils/include-resolver.js +155 -0
- package/dist/emit/utils/index.d.ts +8 -0
- package/dist/emit/utils/index.js +12 -0
- package/dist/emit/utils/type-inference.d.ts +159 -0
- package/dist/emit/utils/type-inference.js +559 -0
- package/dist/eslint-check.d.ts +10 -0
- package/dist/eslint-check.js +102 -0
- package/dist/framework-package.d.ts +10 -0
- package/dist/framework-package.js +58 -0
- package/dist/framework-registry.d.ts +39 -0
- package/dist/framework-registry.js +30 -0
- package/dist/incremental-cache.d.ts +116 -0
- package/dist/incremental-cache.js +335 -0
- package/dist/ir/adc-range-validation.d.ts +11 -0
- package/dist/ir/adc-range-validation.js +250 -0
- package/dist/ir/ast-node-utils.d.ts +8 -0
- package/dist/ir/ast-node-utils.js +32 -0
- package/dist/ir/board-pin-utils.d.ts +13 -0
- package/dist/ir/board-pin-utils.js +65 -0
- package/dist/ir/board-resolver.d.ts +36 -0
- package/dist/ir/board-resolver.js +437 -0
- package/dist/ir/build-ir-state.d.ts +167 -0
- package/dist/ir/build-ir-state.js +306 -0
- package/dist/ir/build-ir.d.ts +2 -0
- package/dist/ir/build-ir.js +808 -0
- package/dist/ir/call-graph.d.ts +35 -0
- package/dist/ir/call-graph.js +194 -0
- package/dist/ir/compile-time-only.d.ts +2 -0
- package/dist/ir/compile-time-only.js +16 -0
- package/dist/ir/declaration-builders.d.ts +9 -0
- package/dist/ir/declaration-builders.js +608 -0
- package/dist/ir/entry-points.d.ts +30 -0
- package/dist/ir/entry-points.js +191 -0
- package/dist/ir/expression-to-ir.d.ts +5 -0
- package/dist/ir/expression-to-ir.js +2249 -0
- package/dist/ir/extract-property-chain.d.ts +10 -0
- package/dist/ir/extract-property-chain.js +30 -0
- package/dist/ir/feature-prescan.d.ts +3 -0
- package/dist/ir/feature-prescan.js +43 -0
- package/dist/ir/feature-registry.d.ts +30 -0
- package/dist/ir/feature-registry.js +816 -0
- package/dist/ir/filter.d.ts +7 -0
- package/dist/ir/filter.js +85 -0
- package/dist/ir/function-builder.d.ts +19 -0
- package/dist/ir/function-builder.js +670 -0
- package/dist/ir/hal/hal-emitter.d.ts +62 -0
- package/dist/ir/hal/hal-emitter.js +735 -0
- package/dist/ir/hal/hal-parser.d.ts +61 -0
- package/dist/ir/hal/hal-parser.js +473 -0
- package/dist/ir/hal/hal-plugins.d.ts +34 -0
- package/dist/ir/hal/hal-plugins.js +706 -0
- package/dist/ir/hal-resolver.d.ts +5 -0
- package/dist/ir/hal-resolver.js +5 -0
- package/dist/ir/heap-analysis.d.ts +8 -0
- package/dist/ir/heap-analysis.js +350 -0
- package/dist/ir/heap-array-validation.d.ts +24 -0
- package/dist/ir/heap-array-validation.js +29 -0
- package/dist/ir/identifier-collector.d.ts +18 -0
- package/dist/ir/identifier-collector.js +439 -0
- package/dist/ir/interrupt-analysis.d.ts +39 -0
- package/dist/ir/interrupt-analysis.js +467 -0
- package/dist/ir/memory-budget-validation.d.ts +10 -0
- package/dist/ir/memory-budget-validation.js +86 -0
- package/dist/ir/namespace-builder.d.ts +7 -0
- package/dist/ir/namespace-builder.js +152 -0
- package/dist/ir/ownership-analysis.d.ts +9 -0
- package/dist/ir/ownership-analysis.js +1207 -0
- package/dist/ir/peripheral-ownership.d.ts +3 -0
- package/dist/ir/peripheral-ownership.js +152 -0
- package/dist/ir/peripheral-symbols.d.ts +7 -0
- package/dist/ir/peripheral-symbols.js +57 -0
- package/dist/ir/peripheral-usage.d.ts +54 -0
- package/dist/ir/peripheral-usage.js +691 -0
- package/dist/ir/peripheral-validation.d.ts +8 -0
- package/dist/ir/peripheral-validation.js +77 -0
- package/dist/ir/pin-alias-conflict.d.ts +4 -0
- package/dist/ir/pin-alias-conflict.js +34 -0
- package/dist/ir/pin-capability-validation.d.ts +6 -0
- package/dist/ir/pin-capability-validation.js +413 -0
- package/dist/ir/pin-mode-validation.d.ts +10 -0
- package/dist/ir/pin-mode-validation.js +301 -0
- package/dist/ir/pin-safety.d.ts +11 -0
- package/dist/ir/pin-safety.js +58 -0
- package/dist/ir/program-analysis.d.ts +44 -0
- package/dist/ir/program-analysis.js +589 -0
- package/dist/ir/pulldown-validation.d.ts +7 -0
- package/dist/ir/pulldown-validation.js +49 -0
- package/dist/ir/pwm-timer-sharing.d.ts +4 -0
- package/dist/ir/pwm-timer-sharing.js +93 -0
- package/dist/ir/reachability.d.ts +68 -0
- package/dist/ir/reachability.js +224 -0
- package/dist/ir/register-decorators.d.ts +6 -0
- package/dist/ir/register-decorators.js +49 -0
- package/dist/ir/render-expr.d.ts +4 -0
- package/dist/ir/render-expr.js +127 -0
- package/dist/ir/resource-analysis.d.ts +19 -0
- package/dist/ir/resource-analysis.js +153 -0
- package/dist/ir/statement-to-ir.d.ts +23 -0
- package/dist/ir/statement-to-ir.js +349 -0
- package/dist/ir/symbol-table.d.ts +68 -0
- package/dist/ir/symbol-table.js +179 -0
- package/dist/ir/symbol-types.d.ts +56 -0
- package/dist/ir/symbol-types.js +107 -0
- package/dist/ir/timer0-pwm-timing-conflict.d.ts +4 -0
- package/dist/ir/timer0-pwm-timing-conflict.js +71 -0
- package/dist/ir/timing-validation.d.ts +7 -0
- package/dist/ir/timing-validation.js +95 -0
- package/dist/ir/transformers/array-methods.d.ts +8 -0
- package/dist/ir/transformers/array-methods.js +493 -0
- package/dist/ir/transformers/call-statement.d.ts +5 -0
- package/dist/ir/transformers/call-statement.js +758 -0
- package/dist/ir/transformers/callback-context-registry.d.ts +20 -0
- package/dist/ir/transformers/callback-context-registry.js +55 -0
- package/dist/ir/transformers/canvas-lowering.d.ts +42 -0
- package/dist/ir/transformers/canvas-lowering.js +249 -0
- package/dist/ir/transformers/control-flow.d.ts +8 -0
- package/dist/ir/transformers/control-flow.js +508 -0
- package/dist/ir/transformers/expressions.d.ts +7 -0
- package/dist/ir/transformers/expressions.js +406 -0
- package/dist/ir/transformers/hal-call-resolver.d.ts +29 -0
- package/dist/ir/transformers/hal-call-resolver.js +274 -0
- package/dist/ir/transformers/hal-emit-helpers.d.ts +14 -0
- package/dist/ir/transformers/hal-emit-helpers.js +63 -0
- package/dist/ir/transformers/namespace-methods.d.ts +20 -0
- package/dist/ir/transformers/namespace-methods.js +110 -0
- package/dist/ir/transformers/register-assignment.d.ts +5 -0
- package/dist/ir/transformers/register-assignment.js +56 -0
- package/dist/ir/transformers/ui-call-resolver.d.ts +149 -0
- package/dist/ir/transformers/ui-call-resolver.js +1136 -0
- package/dist/ir/transformers/ui-callback-lowering.d.ts +62 -0
- package/dist/ir/transformers/ui-callback-lowering.js +623 -0
- package/dist/ir/transformers/ui-mount.d.ts +20 -0
- package/dist/ir/transformers/ui-mount.js +35 -0
- package/dist/ir/transformers/ui-reactive.d.ts +68 -0
- package/dist/ir/transformers/ui-reactive.js +129 -0
- package/dist/ir/transformers/variables.d.ts +14 -0
- package/dist/ir/transformers/variables.js +1045 -0
- package/dist/ir/try-catch-validation.d.ts +26 -0
- package/dist/ir/try-catch-validation.js +128 -0
- package/dist/ir/type-resolution.d.ts +31 -0
- package/dist/ir/type-resolution.js +977 -0
- package/dist/ir/ui-element-auto-wire.d.ts +50 -0
- package/dist/ir/ui-element-auto-wire.js +218 -0
- package/dist/ir/unit-suspicion-validation.d.ts +12 -0
- package/dist/ir/unit-suspicion-validation.js +296 -0
- package/dist/ir/utils/walk-ir.d.ts +6 -0
- package/dist/ir/utils/walk-ir.js +162 -0
- package/dist/ir/validation-orchestrator.d.ts +4 -0
- package/dist/ir/validation-orchestrator.js +45 -0
- package/dist/libdef/base-class-resolver.d.ts +23 -0
- package/dist/libdef/base-class-resolver.js +50 -0
- package/dist/libdef/cpp-to-decl.d.ts +34 -0
- package/dist/libdef/cpp-to-decl.js +584 -0
- package/dist/libdef/header-parser.d.ts +53 -0
- package/dist/libdef/header-parser.js +269 -0
- package/dist/libdef/registry.d.ts +10 -0
- package/dist/libdef/registry.js +142 -0
- package/dist/mapping/peripheral-names.d.ts +23 -0
- package/dist/mapping/peripheral-names.js +63 -0
- package/dist/mapping/source-map.d.ts +46 -0
- package/dist/mapping/source-map.js +207 -0
- package/dist/orchestrator/dts-generator.d.ts +17 -0
- package/dist/orchestrator/dts-generator.js +83 -0
- package/dist/orchestrator/graph-builder.d.ts +18 -0
- package/dist/orchestrator/graph-builder.js +218 -0
- package/dist/orchestrator/semantic-facts-verifier.d.ts +38 -0
- package/dist/orchestrator/semantic-facts-verifier.js +121 -0
- package/dist/orchestrator/semantic-facts.d.ts +141 -0
- package/dist/orchestrator/semantic-facts.js +652 -0
- package/dist/orchestrator/type-checker.d.ts +39 -0
- package/dist/orchestrator/type-checker.js +760 -0
- package/dist/platform/async-runtime.d.ts +8 -0
- package/dist/platform/async-runtime.js +26 -0
- package/dist/platform/generic-strategy.d.ts +101 -0
- package/dist/platform/generic-strategy.js +289 -0
- package/dist/platform/index.d.ts +4 -0
- package/dist/platform/index.js +9 -0
- package/dist/platform/registry.d.ts +21 -0
- package/dist/platform/registry.js +42 -0
- package/dist/platform/toolchain.d.ts +4 -0
- package/dist/platform/toolchain.js +43 -0
- package/dist/preview/client.d.ts +1 -0
- package/dist/preview/client.js +137 -0
- package/dist/preview/server.d.ts +5 -0
- package/dist/preview/server.js +199 -0
- package/dist/profiler/index.d.ts +28 -0
- package/dist/profiler/index.js +28 -0
- package/dist/profiler/profiler.d.ts +95 -0
- package/dist/profiler/profiler.js +338 -0
- package/dist/profiler/types.d.ts +87 -0
- package/dist/profiler/types.js +12 -0
- package/dist/stores/display-profile-store.d.ts +16 -0
- package/dist/stores/display-profile-store.js +48 -0
- package/dist/stores/theme-store.d.ts +13 -0
- package/dist/stores/theme-store.js +30 -0
- package/dist/testing.d.ts +45 -0
- package/dist/testing.js +47 -0
- package/dist/transpile/resolution.d.ts +29 -0
- package/dist/transpile/resolution.js +282 -0
- package/dist/transpile.d.ts +3 -0
- package/dist/transpile.js +722 -0
- package/dist/types.d.ts +217 -0
- package/dist/types.js +1 -0
- package/dist/ui/ui-bridge.d.ts +4 -0
- package/dist/ui/ui-bridge.js +34 -0
- package/dist/ui-hook.d.ts +84 -0
- package/dist/ui-hook.js +45 -0
- package/dist/utils/cli.d.ts +3 -0
- package/dist/utils/cli.js +463 -0
- package/dist/utils/fs.d.ts +4 -0
- package/dist/utils/fs.js +23 -0
- package/dist/utils/logger.d.ts +2 -0
- package/dist/utils/logger.js +8 -0
- package/dist/utils/strings.d.ts +35 -0
- package/dist/utils/strings.js +142 -0
- package/dist/utils/ui.d.ts +66 -0
- package/dist/utils/ui.js +149 -0
- package/dist/watch.d.ts +35 -0
- package/dist/watch.js +111 -0
- package/package.json +127 -0
|
@@ -0,0 +1,758 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import { requiredIncludes, mutableArrayVars, nestedClassAliases, hoistedNestedClasses, topLevelClassNames, topLevelClasses, activeEnumNames, hoistedNestedFunctions, getContext } from "../build-ir-state.js";
|
|
3
|
+
import { getCurrentIrTypeScope } from "../symbol-types.js";
|
|
4
|
+
import { extractNodeComments, makeSourceSpan } from "../ast-node-utils.js";
|
|
5
|
+
import { tryResolveHALMethod } from "./hal-call-resolver.js";
|
|
6
|
+
import { tryResolveUICall, isSignalName, resolveUIModuleImport, recordPressBinding, uiPressBindings, resolveNodeIndex, resolveNodeTag, watchPinSpecs, recordWatchPin, recordClickHandler, clickHandlers, pushUnknownElementDiagnostic } from "./ui-call-resolver.js";
|
|
7
|
+
import { lowerCallbackStatements, resolveCallbackArg, getCanvasAmbientCtx, getCanvasAmbientDiagnostics, getCanvasAmbientSourceText, } from "./ui-callback-lowering.js";
|
|
8
|
+
import { rewriteCanvasCall } from "./canvas-lowering.js";
|
|
9
|
+
import { callbackContextLabel, unsupportedStatementHint } from "./callback-context-registry.js";
|
|
10
|
+
import { tryLowerArrayAndStringMethods } from "./array-methods.js";
|
|
11
|
+
import { expressionToIR } from "../expression-to-ir.js";
|
|
12
|
+
import { lowerStatementList } from "../statement-to-ir.js";
|
|
13
|
+
import { escapeCppKeyword } from "../../utils/strings.js";
|
|
14
|
+
import { renderExprAsText, calleeToText } from "../render-expr.js";
|
|
15
|
+
import { parseCppType, renderCppType, parsedIsPointer, parsedIsMap, parsedIsSet } from "../../api/shared/cpp-type-ir.js";
|
|
16
|
+
/**
|
|
17
|
+
* Resolve a `screen.<id>` or `screen.groups.<screenId>.<id>` element receiver
|
|
18
|
+
* (the object a method like .onClick is called on) into its tree name, element
|
|
19
|
+
* handle, and optional screen-id scope. Returns undefined when the expression
|
|
20
|
+
* is neither shape. The screenId is present only for the grouped form; callers
|
|
21
|
+
* pass it to resolveNodeIndex/resolveNodeTag to scope the search.
|
|
22
|
+
*/
|
|
23
|
+
function resolveElementReceiver(expr) {
|
|
24
|
+
if (!ts.isPropertyAccessExpression(expr))
|
|
25
|
+
return undefined;
|
|
26
|
+
// Flat: screen.<id> → expr.expression is an Identifier ("screen").
|
|
27
|
+
if (ts.isIdentifier(expr.expression)) {
|
|
28
|
+
return { treeName: expr.expression.text, elemId: expr.name.text };
|
|
29
|
+
}
|
|
30
|
+
// Grouped: screen.groups.<screenId>.<id> → expr.expression is
|
|
31
|
+
// PropertyAccess(screen.groups, screenId), i.e. two levels deeper.
|
|
32
|
+
const inner = expr.expression; // screen.groups.<screenId>
|
|
33
|
+
if (ts.isPropertyAccessExpression(inner) &&
|
|
34
|
+
ts.isPropertyAccessExpression(inner.expression) &&
|
|
35
|
+
ts.isIdentifier(inner.expression.expression) &&
|
|
36
|
+
inner.expression.expression.text === "screen" &&
|
|
37
|
+
inner.expression.name.text === "groups") {
|
|
38
|
+
return { treeName: "screen", elemId: expr.name.text, screenId: inner.name.text };
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* When a Map/Set key is an enum-typed expression and the container's key type
|
|
44
|
+
* is an integral type, the lowered `map[key]` / `map.count(key)` / `map.at(key)`
|
|
45
|
+
* would pass the enum operand directly. A C++ `enum class` does not implicitly
|
|
46
|
+
* convert to the map's integral key type, so g++ rejects it ("no match for
|
|
47
|
+
* 'operator[]' ... 'K' to 'const int&'"). Wrap the key in
|
|
48
|
+
* `static_cast<KeyType>(...)` so it matches.
|
|
49
|
+
*
|
|
50
|
+
* This handles BOTH shapes of enum-typed key operand:
|
|
51
|
+
* - an enum member access (`Color.Red`, `Op.Inc`) — detected via
|
|
52
|
+
* `activeEnumNames` on the object identifier; and
|
|
53
|
+
* - a bare identifier whose declared type is an enum (`k` where `k: K`) —
|
|
54
|
+
* resolved through the in-scope IR type map (the gap demo #28 Finding E
|
|
55
|
+
* surfaced: previously only enum-member access was cast, so a bare
|
|
56
|
+
* enum-typed key variable on `.set`/`.has`/`.get`/`.delete` compiled to an
|
|
57
|
+
* uncast `map[k]`/`map.count(k)`/`map.at(k)` and failed at g++ time).
|
|
58
|
+
*/
|
|
59
|
+
function castEnumKeyIfNeeded(keyText, keyNode, receiverType) {
|
|
60
|
+
// Only relevant for std::map/std::set with an integral key type.
|
|
61
|
+
if (!parsedIsMap(receiverType) && !parsedIsSet(receiverType))
|
|
62
|
+
return keyText;
|
|
63
|
+
const containerIr = parseCppType(receiverType);
|
|
64
|
+
const keyType = containerIr.kind === "map" ? renderCppType(containerIr.key) : "";
|
|
65
|
+
const isIntegral = /^(int|int8_t|int16_t|int32_t|int64_t|uint8_t|uint16_t|uint32_t|uint64_t|size_t|long|short|unsigned|char)$/.test(keyType);
|
|
66
|
+
if (!isIntegral)
|
|
67
|
+
return keyText;
|
|
68
|
+
// Detect an enum-typed key operand.
|
|
69
|
+
let isEnum = false;
|
|
70
|
+
if (ts.isPropertyAccessExpression(keyNode) && ts.isIdentifier(keyNode.expression)) {
|
|
71
|
+
// Enum member access: `Color.Red`.
|
|
72
|
+
isEnum = activeEnumNames.has(keyNode.expression.text);
|
|
73
|
+
}
|
|
74
|
+
else if (ts.isIdentifier(keyNode)) {
|
|
75
|
+
// Bare identifier: resolve its declared type through the IR type scope.
|
|
76
|
+
// If it is an enum name, the operand is enum-typed and needs the cast.
|
|
77
|
+
const varType = getCurrentIrTypeScope()?.locals.get(keyNode.text) ?? getCurrentIrTypeScope()?.globals.get(keyNode.text);
|
|
78
|
+
if (varType && activeEnumNames.has(varType)) {
|
|
79
|
+
isEnum = true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (!isEnum)
|
|
83
|
+
return keyText;
|
|
84
|
+
return `static_cast<${keyType}>(${keyText})`;
|
|
85
|
+
}
|
|
86
|
+
/** Lower a void UI event callback (onClick/onHold/onRelease/arity-1 onChange)
|
|
87
|
+
* to bodyStatements + optional named-ref metadata. */
|
|
88
|
+
function lowerVoidEventCallback(cbArg, fileName, sourceText, diagnostics, label) {
|
|
89
|
+
const resolved = resolveCallbackArg(cbArg, cbArg?.getSourceFile(), diagnostics, label);
|
|
90
|
+
if (!resolved)
|
|
91
|
+
return {};
|
|
92
|
+
const sourceSpan = cbArg ? makeSourceSpan(cbArg, fileName, sourceText) : undefined;
|
|
93
|
+
if (resolved.kind === "inline") {
|
|
94
|
+
return {
|
|
95
|
+
bodyStatements: lowerCallbackStatements(resolved.fn, fileName, sourceText, diagnostics, "ui-event-callback"),
|
|
96
|
+
sourceSpan,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return { isNamedRef: true, namedFn: resolved.name, sourceSpan };
|
|
100
|
+
}
|
|
101
|
+
/** Lower an optional callback for watchPin-style wrappers that have a prelude
|
|
102
|
+
* (onToggle / onChange(pin,count)). Named refs become a call after prelude. */
|
|
103
|
+
function lowerPreludeCallback(cbArg, fileName, sourceText, diagnostics, label, prelude) {
|
|
104
|
+
const resolved = resolveCallbackArg(cbArg, cbArg?.getSourceFile(), diagnostics, label);
|
|
105
|
+
const sourceSpan = cbArg ? makeSourceSpan(cbArg, fileName, sourceText) : undefined;
|
|
106
|
+
if (!resolved)
|
|
107
|
+
return { bodyStatements: prelude, sourceSpan };
|
|
108
|
+
if (resolved.kind === "inline") {
|
|
109
|
+
return {
|
|
110
|
+
bodyStatements: [
|
|
111
|
+
...prelude,
|
|
112
|
+
...lowerCallbackStatements(resolved.fn, fileName, sourceText, diagnostics, "ui-event-callback"),
|
|
113
|
+
],
|
|
114
|
+
sourceSpan,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
bodyStatements: [
|
|
119
|
+
...prelude,
|
|
120
|
+
{
|
|
121
|
+
kind: "call",
|
|
122
|
+
sourceSpan: sourceSpan ?? makeSourceSpan(cbArg, fileName, sourceText),
|
|
123
|
+
callee: resolved.name,
|
|
124
|
+
args: [],
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
sourceSpan,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
export function callToStatement(statementNode, call, fileName, sourceText, diagnostics, pointerVars = new Map()) {
|
|
131
|
+
const comments = extractNodeComments(statementNode, sourceText);
|
|
132
|
+
// ── setInterval/setTimeout arrow-callback hoisting (BEFORE HAL) ────────
|
|
133
|
+
// The timer helpers are runtime utilities, never HAL methods, but the HAL
|
|
134
|
+
// resolver treats bare-identifier globals as pseudo-HAL calls and claims
|
|
135
|
+
// them — baking the arrow arg into a lambda placeholder before our hoist
|
|
136
|
+
// can run. Intercept timer calls with arrow callbacks FIRST, hoist the
|
|
137
|
+
// arrow to a named function, and short-circuit before HAL sees it.
|
|
138
|
+
if (ts.isIdentifier(call.expression) && TIMER_CALLEES.has(call.expression.text)) {
|
|
139
|
+
const hoisted = hoistTimerArrowArg(call, fileName, sourceText, diagnostics, pointerVars, comments);
|
|
140
|
+
if (hoisted)
|
|
141
|
+
return hoisted;
|
|
142
|
+
}
|
|
143
|
+
// ── Ambient canvas ctx: rewrite ctx.method(...) while drawCanvas lowers ──
|
|
144
|
+
{
|
|
145
|
+
const canvasCtx = getCanvasAmbientCtx();
|
|
146
|
+
if (canvasCtx) {
|
|
147
|
+
const ambientDiags = getCanvasAmbientDiagnostics() ?? diagnostics;
|
|
148
|
+
const ambientSrc = getCanvasAmbientSourceText() ?? sourceText;
|
|
149
|
+
if (ts.isPropertyAccessExpression(call.expression) &&
|
|
150
|
+
ts.isIdentifier(call.expression.expression) &&
|
|
151
|
+
call.expression.expression.text === canvasCtx) {
|
|
152
|
+
const rewritten = rewriteCanvasCall(call, canvasCtx, ambientDiags, ambientSrc);
|
|
153
|
+
return {
|
|
154
|
+
kind: "call",
|
|
155
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
156
|
+
leadingComments: comments.leadingComments,
|
|
157
|
+
trailingComments: comments.trailingComments,
|
|
158
|
+
callee: "__EMIT__",
|
|
159
|
+
args: [{ kind: "string", value: (rewritten ?? "").replace(/;$/, "") }],
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
ambientDiags.push({
|
|
163
|
+
severity: "error",
|
|
164
|
+
code: "ui-callback-unsupported-statement",
|
|
165
|
+
message: `Unsupported statement in ${callbackContextLabel("ui-draw-canvas")}: only ctx.* drawing calls are allowed here.`,
|
|
166
|
+
hint: unsupportedStatementHint("ui-draw-canvas"),
|
|
167
|
+
source: ambientSrc.slice(Math.max(0, call.getStart() - 40), call.getEnd() + 10).trim(),
|
|
168
|
+
});
|
|
169
|
+
return {
|
|
170
|
+
kind: "block",
|
|
171
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
172
|
+
leadingComments: comments.leadingComments,
|
|
173
|
+
trailingComments: comments.trailingComments,
|
|
174
|
+
body: [],
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// ── signal.set(value) → assignment ─────────────────────────────────────
|
|
179
|
+
// A signal lowers to a plain device variable; its .set() method is just an
|
|
180
|
+
// assignment. Intercept before HAL so it doesn't try to resolve .set as a
|
|
181
|
+
// HAL method on the variable.
|
|
182
|
+
if (ts.isPropertyAccessExpression(call.expression) &&
|
|
183
|
+
call.expression.name.text === "set" &&
|
|
184
|
+
ts.isIdentifier(call.expression.expression) &&
|
|
185
|
+
isSignalName(call.expression.expression.text)) {
|
|
186
|
+
const valueIR = call.arguments[0] ? expressionToIR(call.arguments[0], sourceText, diagnostics, pointerVars) : { kind: "number", value: 0 };
|
|
187
|
+
return {
|
|
188
|
+
kind: "assign",
|
|
189
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
190
|
+
leadingComments: comments.leadingComments,
|
|
191
|
+
trailingComments: comments.trailingComments,
|
|
192
|
+
target: call.expression.expression.text,
|
|
193
|
+
operator: "=",
|
|
194
|
+
value: valueIR,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
// ── screen.btn.onPress(pin) / onRelease(pin) ───────────────────────────
|
|
198
|
+
// Lowers to a press-binding record: the emit layer emits a handler function
|
|
199
|
+
// (ui_on_press(nodeIndex)) and an attachInterrupt in setup(). The call shape
|
|
200
|
+
// is <tree>.<id>.onPress(<pin>) — a chained property access on an imported
|
|
201
|
+
// UI tree element.
|
|
202
|
+
if (ts.isPropertyAccessExpression(call.expression) &&
|
|
203
|
+
(call.expression.name.text === "onPress" || call.expression.name.text === "onRelease") &&
|
|
204
|
+
ts.isPropertyAccessExpression(call.expression.expression) &&
|
|
205
|
+
ts.isIdentifier(call.expression.expression.expression)) {
|
|
206
|
+
const treeName = call.expression.expression.expression.text;
|
|
207
|
+
const elemId = call.expression.expression.name.text;
|
|
208
|
+
const edge = call.expression.name.text === "onPress" ? "press" : "release";
|
|
209
|
+
const pinArg = call.arguments[0];
|
|
210
|
+
const pinText = pinArg ? (ts.isIdentifier(pinArg) ? pinArg.text : String(pinArg.getText())) : "0";
|
|
211
|
+
const htmlPath = resolveUIModuleImport(treeName);
|
|
212
|
+
const nodeIndex = htmlPath ? resolveNodeIndex(htmlPath, elemId) : 0;
|
|
213
|
+
if (nodeIndex < 0) {
|
|
214
|
+
pushUnknownElementDiagnostic(diagnostics, `screen.${elemId}.${call.expression.name.text}`, elemId, treeName);
|
|
215
|
+
return { kind: "block", sourceSpan: makeSourceSpan(call, fileName, sourceText), leadingComments: comments.leadingComments, trailingComments: comments.trailingComments, body: [] };
|
|
216
|
+
}
|
|
217
|
+
const handlerName = `__ui_${elemId}_${edge}_${uiPressBindings().length}`;
|
|
218
|
+
recordPressBinding({ nodeIndex, pin: pinText, edge, handlerName });
|
|
219
|
+
return {
|
|
220
|
+
kind: "block",
|
|
221
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
222
|
+
leadingComments: comments.leadingComments,
|
|
223
|
+
trailingComments: comments.trailingComments,
|
|
224
|
+
body: [],
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
// ── screen.led.onToggle(pin, callback) — checkbox toggle ──────────────
|
|
228
|
+
// Flips the node's checked state on falling edge and optionally calls
|
|
229
|
+
// the user's callback (for signal writes). Uses the pin-watching system.
|
|
230
|
+
if (ts.isPropertyAccessExpression(call.expression) &&
|
|
231
|
+
call.expression.name.text === "onToggle" &&
|
|
232
|
+
ts.isPropertyAccessExpression(call.expression.expression) &&
|
|
233
|
+
ts.isIdentifier(call.expression.expression.expression)) {
|
|
234
|
+
const treeName = call.expression.expression.expression.text;
|
|
235
|
+
const elemId = call.expression.expression.name.text;
|
|
236
|
+
const pinArg = call.arguments[0];
|
|
237
|
+
const cbArg = call.arguments[1];
|
|
238
|
+
const pin = pinArg ? (ts.isNumericLiteral(pinArg) ? pinArg.text : pinArg.getText()) : "0";
|
|
239
|
+
const htmlPath = resolveUIModuleImport(treeName);
|
|
240
|
+
const nodeIndex = htmlPath ? resolveNodeIndex(htmlPath, elemId) : 0;
|
|
241
|
+
if (nodeIndex < 0) {
|
|
242
|
+
pushUnknownElementDiagnostic(diagnostics, `screen.${elemId}.${call.expression.name.text}`, elemId, treeName);
|
|
243
|
+
return { kind: "block", sourceSpan: makeSourceSpan(call, fileName, sourceText), leadingComments: comments.leadingComments, trailingComments: comments.trailingComments, body: [] };
|
|
244
|
+
}
|
|
245
|
+
const fnName = `__ui_${elemId}_toggle_${watchPinSpecs().length}`;
|
|
246
|
+
const prelude = [
|
|
247
|
+
{
|
|
248
|
+
kind: "call",
|
|
249
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
250
|
+
callee: "__EMIT__",
|
|
251
|
+
args: [{
|
|
252
|
+
kind: "string",
|
|
253
|
+
value: `__ui_nodes[${nodeIndex}].value = !__ui_nodes[${nodeIndex}].value; ui_mark_dirty(${nodeIndex})`,
|
|
254
|
+
}],
|
|
255
|
+
},
|
|
256
|
+
];
|
|
257
|
+
const { bodyStatements, sourceSpan } = lowerPreludeCallback(cbArg, fileName, sourceText, diagnostics, `screen.${elemId}.onToggle`, prelude);
|
|
258
|
+
recordWatchPin({ pin: String(pin), fnName, callbackBody: "", bodyStatements, sourceSpan });
|
|
259
|
+
return {
|
|
260
|
+
kind: "block",
|
|
261
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
262
|
+
leadingComments: comments.leadingComments,
|
|
263
|
+
trailingComments: comments.trailingComments,
|
|
264
|
+
body: [],
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
// ── screen.input.onChange(callback) — input text committed via keyboard ─
|
|
268
|
+
// Distinct from the GPIO onChange below: this variant takes a single callback
|
|
269
|
+
// arg (no pin/count) and fires after ui_kb_close commits the typed text.
|
|
270
|
+
// Also handles <range> onChange — routed to "rangechange" and fired from the
|
|
271
|
+
// drag loop on every value change during a slider drag.
|
|
272
|
+
if (ts.isPropertyAccessExpression(call.expression) &&
|
|
273
|
+
call.expression.name.text === "onChange" &&
|
|
274
|
+
ts.isPropertyAccessExpression(call.expression.expression) &&
|
|
275
|
+
ts.isIdentifier(call.expression.expression.expression) &&
|
|
276
|
+
call.arguments.length === 1) {
|
|
277
|
+
const treeName = call.expression.expression.expression.text;
|
|
278
|
+
const elemId = call.expression.expression.name.text;
|
|
279
|
+
const cbArg = call.arguments[0];
|
|
280
|
+
const htmlPath = resolveUIModuleImport(treeName);
|
|
281
|
+
const nodeIndex = htmlPath ? resolveNodeIndex(htmlPath, elemId) : 0;
|
|
282
|
+
if (nodeIndex < 0) {
|
|
283
|
+
pushUnknownElementDiagnostic(diagnostics, `screen.${elemId}.${call.expression.name.text}`, elemId, treeName);
|
|
284
|
+
return { kind: "block", sourceSpan: makeSourceSpan(call, fileName, sourceText), leadingComments: comments.leadingComments, trailingComments: comments.trailingComments, body: [] };
|
|
285
|
+
}
|
|
286
|
+
const nodeTag = htmlPath ? resolveNodeTag(htmlPath, elemId) : "";
|
|
287
|
+
const kind = nodeTag === "range" ? "rangechange" : "change";
|
|
288
|
+
const lowered = lowerVoidEventCallback(cbArg, fileName, sourceText, diagnostics, `screen.${elemId}.onChange`);
|
|
289
|
+
const fnName = lowered.isNamedRef && lowered.namedFn
|
|
290
|
+
? lowered.namedFn
|
|
291
|
+
: `__ui_${elemId}_${kind}_${clickHandlers().length}`;
|
|
292
|
+
recordClickHandler({
|
|
293
|
+
nodeIndex,
|
|
294
|
+
kind,
|
|
295
|
+
fnName,
|
|
296
|
+
callbackBody: "",
|
|
297
|
+
bodyStatements: lowered.bodyStatements,
|
|
298
|
+
isNamedRef: lowered.isNamedRef,
|
|
299
|
+
sourceSpan: lowered.sourceSpan,
|
|
300
|
+
});
|
|
301
|
+
return {
|
|
302
|
+
kind: "block",
|
|
303
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
304
|
+
leadingComments: comments.leadingComments,
|
|
305
|
+
trailingComments: comments.trailingComments,
|
|
306
|
+
body: [],
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
// ── screen.modeSelect.onChange(pin, optionCount, callback?) ───────────
|
|
310
|
+
// Cycles .value through 0..optionCount-1 on each falling edge.
|
|
311
|
+
if (ts.isPropertyAccessExpression(call.expression) &&
|
|
312
|
+
call.expression.name.text === "onChange" &&
|
|
313
|
+
ts.isPropertyAccessExpression(call.expression.expression) &&
|
|
314
|
+
ts.isIdentifier(call.expression.expression.expression)) {
|
|
315
|
+
const treeName = call.expression.expression.expression.text;
|
|
316
|
+
const elemId = call.expression.expression.name.text;
|
|
317
|
+
const pinArg = call.arguments[0];
|
|
318
|
+
const countArg = call.arguments[1];
|
|
319
|
+
const cbArg = call.arguments[2];
|
|
320
|
+
const pin = pinArg ? (ts.isNumericLiteral(pinArg) ? pinArg.text : pinArg.getText()) : "0";
|
|
321
|
+
const optionCount = countArg ? (ts.isNumericLiteral(countArg) ? countArg.text : "2") : "2";
|
|
322
|
+
const htmlPath = resolveUIModuleImport(treeName);
|
|
323
|
+
const nodeIndex = htmlPath ? resolveNodeIndex(htmlPath, elemId) : 0;
|
|
324
|
+
if (nodeIndex < 0) {
|
|
325
|
+
pushUnknownElementDiagnostic(diagnostics, `screen.${elemId}.${call.expression.name.text}`, elemId, treeName);
|
|
326
|
+
return { kind: "block", sourceSpan: makeSourceSpan(call, fileName, sourceText), leadingComments: comments.leadingComments, trailingComments: comments.trailingComments, body: [] };
|
|
327
|
+
}
|
|
328
|
+
const fnName = `__ui_${elemId}_change_${watchPinSpecs().length}`;
|
|
329
|
+
const prelude = [
|
|
330
|
+
{
|
|
331
|
+
kind: "call",
|
|
332
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
333
|
+
callee: "__EMIT__",
|
|
334
|
+
args: [{
|
|
335
|
+
kind: "string",
|
|
336
|
+
value: `__ui_nodes[${nodeIndex}].value = (__ui_nodes[${nodeIndex}].value + 1) % ${optionCount}; ui_mark_dirty(${nodeIndex})`,
|
|
337
|
+
}],
|
|
338
|
+
},
|
|
339
|
+
];
|
|
340
|
+
const { bodyStatements, sourceSpan } = lowerPreludeCallback(cbArg, fileName, sourceText, diagnostics, `screen.${elemId}.onChange`, prelude);
|
|
341
|
+
recordWatchPin({ pin: String(pin), fnName, callbackBody: "", bodyStatements, sourceSpan });
|
|
342
|
+
return {
|
|
343
|
+
kind: "block",
|
|
344
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
345
|
+
leadingComments: comments.leadingComments,
|
|
346
|
+
trailingComments: comments.trailingComments,
|
|
347
|
+
body: [],
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
// ── screen.element.onClick(callback?) — touch click handler ──────────
|
|
351
|
+
// Records a click handler for touch hit-testing. No pin needed — the
|
|
352
|
+
// touch poll loop calls ui_handle_touch which hit-tests and dispatches.
|
|
353
|
+
// Supports both flat (screen.<id>) and grouped (screen.groups.<sid>.<id>).
|
|
354
|
+
if (ts.isPropertyAccessExpression(call.expression) &&
|
|
355
|
+
call.expression.name.text === "onClick") {
|
|
356
|
+
const recv = resolveElementReceiver(call.expression.expression);
|
|
357
|
+
if (recv) {
|
|
358
|
+
const treeName = recv.treeName;
|
|
359
|
+
const elemId = recv.elemId;
|
|
360
|
+
const cbArg = call.arguments[0];
|
|
361
|
+
const htmlPath = resolveUIModuleImport(treeName);
|
|
362
|
+
const nodeIndex = htmlPath ? resolveNodeIndex(htmlPath, elemId, recv.screenId) : 0;
|
|
363
|
+
if (nodeIndex < 0) {
|
|
364
|
+
pushUnknownElementDiagnostic(diagnostics, `screen.${elemId}.${call.expression.name.text}`, elemId, treeName);
|
|
365
|
+
return { kind: "block", sourceSpan: makeSourceSpan(call, fileName, sourceText), leadingComments: comments.leadingComments, trailingComments: comments.trailingComments, body: [] };
|
|
366
|
+
}
|
|
367
|
+
const lowered = lowerVoidEventCallback(cbArg, fileName, sourceText, diagnostics, `screen.${elemId}.onClick`);
|
|
368
|
+
const fnName = lowered.isNamedRef && lowered.namedFn
|
|
369
|
+
? lowered.namedFn
|
|
370
|
+
: `__ui_${elemId}_click_${clickHandlers().length}`;
|
|
371
|
+
recordClickHandler({
|
|
372
|
+
nodeIndex,
|
|
373
|
+
kind: "click",
|
|
374
|
+
fnName,
|
|
375
|
+
callbackBody: "",
|
|
376
|
+
bodyStatements: lowered.bodyStatements,
|
|
377
|
+
isNamedRef: lowered.isNamedRef,
|
|
378
|
+
sourceSpan: lowered.sourceSpan,
|
|
379
|
+
});
|
|
380
|
+
return {
|
|
381
|
+
kind: "block",
|
|
382
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
383
|
+
leadingComments: comments.leadingComments,
|
|
384
|
+
trailingComments: comments.trailingComments,
|
|
385
|
+
body: [],
|
|
386
|
+
};
|
|
387
|
+
} // end if (recv)
|
|
388
|
+
}
|
|
389
|
+
// ── screen.element.onHold/onRelease — touch long-press + release handlers ─
|
|
390
|
+
if (ts.isPropertyAccessExpression(call.expression) &&
|
|
391
|
+
(call.expression.name.text === "onHold" || call.expression.name.text === "onRelease") &&
|
|
392
|
+
ts.isPropertyAccessExpression(call.expression.expression) &&
|
|
393
|
+
ts.isIdentifier(call.expression.expression.expression)) {
|
|
394
|
+
const kind = call.expression.name.text === "onHold" ? "hold" : "release";
|
|
395
|
+
const treeName = call.expression.expression.expression.text;
|
|
396
|
+
const elemId = call.expression.expression.name.text;
|
|
397
|
+
const cbArg = call.arguments[0];
|
|
398
|
+
const htmlPath = resolveUIModuleImport(treeName);
|
|
399
|
+
const nodeIndex = htmlPath ? resolveNodeIndex(htmlPath, elemId) : 0;
|
|
400
|
+
if (nodeIndex < 0) {
|
|
401
|
+
pushUnknownElementDiagnostic(diagnostics, `screen.${elemId}.${call.expression.name.text}`, elemId, treeName);
|
|
402
|
+
return { kind: "block", sourceSpan: makeSourceSpan(call, fileName, sourceText), leadingComments: comments.leadingComments, trailingComments: comments.trailingComments, body: [] };
|
|
403
|
+
}
|
|
404
|
+
const lowered = lowerVoidEventCallback(cbArg, fileName, sourceText, diagnostics, `screen.${elemId}.${call.expression.name.text}`);
|
|
405
|
+
const fnName = lowered.isNamedRef && lowered.namedFn
|
|
406
|
+
? lowered.namedFn
|
|
407
|
+
: `__ui_${elemId}_${kind}_${clickHandlers().length}`;
|
|
408
|
+
recordClickHandler({
|
|
409
|
+
nodeIndex,
|
|
410
|
+
kind,
|
|
411
|
+
fnName,
|
|
412
|
+
callbackBody: "",
|
|
413
|
+
bodyStatements: lowered.bodyStatements,
|
|
414
|
+
isNamedRef: lowered.isNamedRef,
|
|
415
|
+
sourceSpan: lowered.sourceSpan,
|
|
416
|
+
});
|
|
417
|
+
return {
|
|
418
|
+
kind: "block",
|
|
419
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
420
|
+
leadingComments: comments.leadingComments,
|
|
421
|
+
trailingComments: comments.trailingComments,
|
|
422
|
+
body: [],
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
// ── emit() / rawCpp() — compile-time C++ injection ─────────────────────
|
|
426
|
+
// Must be BEFORE the HAL resolver — emit/rawCpp are exported from @typecad/hal
|
|
427
|
+
// but are NOT HAL class methods. The HAL resolver would create a pseudo-instance
|
|
428
|
+
// and fail to find a method body, leaving the call unresolved.
|
|
429
|
+
if (ts.isIdentifier(call.expression) && (call.expression.text === "emit" || call.expression.text === "rawCpp")) {
|
|
430
|
+
return {
|
|
431
|
+
kind: "call",
|
|
432
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
433
|
+
leadingComments: comments.leadingComments,
|
|
434
|
+
trailingComments: comments.trailingComments,
|
|
435
|
+
callee: "__EMIT__",
|
|
436
|
+
args: call.arguments.map(a => expressionToIR(a, sourceText, diagnostics, pointerVars)),
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
// ---- HAL method resolver (highest priority) ---
|
|
440
|
+
const halResolved = tryResolveHALMethod(call, fileName, sourceText, diagnostics, pointerVars);
|
|
441
|
+
if (halResolved)
|
|
442
|
+
return halResolved;
|
|
443
|
+
// ---- ui.mount / ui.signal / ui.bind — UI authoring calls ---
|
|
444
|
+
// Handles statement-position ui.* calls. const X = ui.signal(...) (a
|
|
445
|
+
// VariableDeclaration, not an ExpressionStatement) is handled in the
|
|
446
|
+
// variable-declaration path; here we see bare ui.signal(...) statements
|
|
447
|
+
// and synthesize a signal name.
|
|
448
|
+
const uiResolved = tryResolveUICall(call, fileName, sourceText, diagnostics);
|
|
449
|
+
if (uiResolved)
|
|
450
|
+
return uiResolved;
|
|
451
|
+
// Handle super() calls in constructors - transform to super_call IR for class emitter
|
|
452
|
+
if (call.expression.kind === ts.SyntaxKind.SuperKeyword) {
|
|
453
|
+
return {
|
|
454
|
+
kind: "super_call",
|
|
455
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
456
|
+
leadingComments: comments.leadingComments,
|
|
457
|
+
trailingComments: comments.trailingComments,
|
|
458
|
+
args: call.arguments.map(a => expressionToIR(a, sourceText, diagnostics, pointerVars)),
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
// ── include() — compile-time C++ header registration ─────────────────────
|
|
462
|
+
if (ts.isIdentifier(call.expression) && call.expression.text === "include") {
|
|
463
|
+
const firstArg = call.arguments[0];
|
|
464
|
+
if (firstArg && ts.isStringLiteral(firstArg)) {
|
|
465
|
+
requiredIncludes.add(firstArg.text);
|
|
466
|
+
}
|
|
467
|
+
// Emit nothing in C++ — return empty block
|
|
468
|
+
return {
|
|
469
|
+
kind: "block",
|
|
470
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
471
|
+
leadingComments: comments.leadingComments,
|
|
472
|
+
trailingComments: comments.trailingComments,
|
|
473
|
+
body: [],
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
// ── Structural vector/array method lowering (shared with expressionToIR) ─
|
|
477
|
+
// Lower .push/.pop/.map/.filter/.splice/... on a std::vector receiver to the
|
|
478
|
+
// __tc_* helper form (or push_back) BEFORE the generic call handling below.
|
|
479
|
+
// This mirrors expressionToIR's call to tryLowerArrayAndStringMethods, so the
|
|
480
|
+
// statement form (`out.push(x);`) and the expression form (`x = arr.pop()`)
|
|
481
|
+
// share one structural lowering. Previously only the StaticArray case was
|
|
482
|
+
// handled here (mutableArrayVars branch below); the std::vector case was left
|
|
483
|
+
// as a raw `out.push` callee and patched to `push_back` by a regex in the
|
|
484
|
+
// native strategy's normalizeRawExpression. Demo #22 / Tier-2 cleanup.
|
|
485
|
+
const lowered = tryLowerArrayAndStringMethods(call, sourceText, diagnostics, pointerVars);
|
|
486
|
+
if (lowered) {
|
|
487
|
+
if (lowered.kind === "method-call") {
|
|
488
|
+
// Callback-arg methods (.map/.filter/.sort(fn)/...): preserve the
|
|
489
|
+
// structured method-call node as a `call` statement so (a) the emit-time
|
|
490
|
+
// callback hoister finds the callback in `args` and names it, and (b)
|
|
491
|
+
// program-analysis detects the __tc_* helper usage from `callee`.
|
|
492
|
+
return {
|
|
493
|
+
kind: "call",
|
|
494
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
495
|
+
leadingComments: comments.leadingComments,
|
|
496
|
+
trailingComments: comments.trailingComments,
|
|
497
|
+
callee: lowered.callee,
|
|
498
|
+
args: lowered.args,
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
// Value-arg methods (.push/.pop/.fill/.concat/...): the lowering produced a
|
|
502
|
+
// fully-formed C++ expression (e.g. `out.push_back(x)` or `__tc_pop(arr)`);
|
|
503
|
+
// emit it as a raw statement. The __tc_* helper usage is detected by
|
|
504
|
+
// program-analysis via the `includes(name)` check on the call callee.
|
|
505
|
+
const loweredText = renderExprAsText(lowered);
|
|
506
|
+
return {
|
|
507
|
+
kind: "call",
|
|
508
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
509
|
+
leadingComments: comments.leadingComments,
|
|
510
|
+
trailingComments: comments.trailingComments,
|
|
511
|
+
callee: "__RAW_STMT__" + loweredText,
|
|
512
|
+
args: [],
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
// ── Array method translation: push → push_back, pop → pop_back ──────────
|
|
516
|
+
if (ts.isPropertyAccessExpression(call.expression)) {
|
|
517
|
+
const methodName = call.expression.name.text;
|
|
518
|
+
const objExpr = call.expression.expression;
|
|
519
|
+
if (ts.isIdentifier(objExpr) && mutableArrayVars.has(objExpr.text)) {
|
|
520
|
+
if (methodName === "push") {
|
|
521
|
+
return {
|
|
522
|
+
kind: "call",
|
|
523
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
524
|
+
leadingComments: comments.leadingComments,
|
|
525
|
+
trailingComments: comments.trailingComments,
|
|
526
|
+
callee: `${objExpr.text}.push`,
|
|
527
|
+
args: call.arguments.map(a => expressionToIR(a, sourceText, diagnostics, pointerVars)),
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
if (methodName === "pop") {
|
|
531
|
+
return {
|
|
532
|
+
kind: "call",
|
|
533
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
534
|
+
leadingComments: comments.leadingComments,
|
|
535
|
+
trailingComments: comments.trailingComments,
|
|
536
|
+
callee: `${objExpr.text}.pop`,
|
|
537
|
+
args: [],
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
// ── Map/Set method lowering: .set()/.get()/.has()/.delete()/.add() ──────
|
|
543
|
+
if (ts.isPropertyAccessExpression(call.expression)) {
|
|
544
|
+
const mapMethodName = call.expression.name.text;
|
|
545
|
+
if ((mapMethodName === "set" || mapMethodName === "get" || mapMethodName === "has" || mapMethodName === "delete" || mapMethodName === "add") && call.arguments.length >= 1) {
|
|
546
|
+
const mapReceiver = call.expression.expression;
|
|
547
|
+
let receiverType;
|
|
548
|
+
if (ts.isIdentifier(mapReceiver)) {
|
|
549
|
+
receiverType = getCurrentIrTypeScope()?.locals.get(mapReceiver.text) ?? getCurrentIrTypeScope()?.globals.get(mapReceiver.text);
|
|
550
|
+
}
|
|
551
|
+
else if (ts.isPropertyAccessExpression(mapReceiver) && mapReceiver.expression.kind === ts.SyntaxKind.ThisKeyword) {
|
|
552
|
+
receiverType = getCurrentIrTypeScope()?.locals.get(`this->${mapReceiver.name.text}`);
|
|
553
|
+
}
|
|
554
|
+
if (receiverType && (parsedIsMap(receiverType) || parsedIsSet(receiverType))) {
|
|
555
|
+
const recIR = expressionToIR(mapReceiver, sourceText, diagnostics, pointerVars);
|
|
556
|
+
const recText = renderExprAsText(recIR);
|
|
557
|
+
const arg0IR = expressionToIR(call.arguments[0], sourceText, diagnostics, pointerVars);
|
|
558
|
+
const arg0Text = renderExprAsText(arg0IR);
|
|
559
|
+
if (parsedIsMap(receiverType) && mapMethodName === "set" && call.arguments.length >= 2) {
|
|
560
|
+
const arg1IR = expressionToIR(call.arguments[1], sourceText, diagnostics, pointerVars);
|
|
561
|
+
const arg1Text = renderExprAsText(arg1IR);
|
|
562
|
+
const castedKey = castEnumKeyIfNeeded(arg0Text, call.arguments[0], receiverType);
|
|
563
|
+
return {
|
|
564
|
+
kind: "assign",
|
|
565
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
566
|
+
leadingComments: comments.leadingComments,
|
|
567
|
+
trailingComments: comments.trailingComments,
|
|
568
|
+
target: `${recText}[${castedKey}]`,
|
|
569
|
+
operator: "=",
|
|
570
|
+
value: { kind: "raw", value: arg1Text },
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
if (parsedIsMap(receiverType) && mapMethodName === "get") {
|
|
574
|
+
const castedKey = castEnumKeyIfNeeded(arg0Text, call.arguments[0], receiverType);
|
|
575
|
+
return {
|
|
576
|
+
kind: "call",
|
|
577
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
578
|
+
leadingComments: comments.leadingComments,
|
|
579
|
+
trailingComments: comments.trailingComments,
|
|
580
|
+
callee: `${recText}.at`,
|
|
581
|
+
args: [{ kind: "raw", value: castedKey }],
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
if ((parsedIsMap(receiverType) || parsedIsSet(receiverType)) && mapMethodName === "has") {
|
|
585
|
+
const castedKey = castEnumKeyIfNeeded(arg0Text, call.arguments[0], receiverType);
|
|
586
|
+
return {
|
|
587
|
+
kind: "call",
|
|
588
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
589
|
+
leadingComments: comments.leadingComments,
|
|
590
|
+
trailingComments: comments.trailingComments,
|
|
591
|
+
callee: `${recText}.count`,
|
|
592
|
+
args: [{ kind: "raw", value: castedKey }],
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
if ((parsedIsMap(receiverType) || parsedIsSet(receiverType)) && mapMethodName === "delete") {
|
|
596
|
+
const castedKey = castEnumKeyIfNeeded(arg0Text, call.arguments[0], receiverType);
|
|
597
|
+
return {
|
|
598
|
+
kind: "call",
|
|
599
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
600
|
+
leadingComments: comments.leadingComments,
|
|
601
|
+
trailingComments: comments.trailingComments,
|
|
602
|
+
callee: `${recText}.erase`,
|
|
603
|
+
args: [{ kind: "raw", value: castedKey }],
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
if (parsedIsSet(receiverType) && mapMethodName === "add") {
|
|
607
|
+
return {
|
|
608
|
+
kind: "call",
|
|
609
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
610
|
+
leadingComments: comments.leadingComments,
|
|
611
|
+
trailingComments: comments.trailingComments,
|
|
612
|
+
callee: `${recText}.insert`,
|
|
613
|
+
args: [{ kind: "raw", value: arg0Text }],
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
// Format callee, using -> for pointer variables
|
|
620
|
+
let calleeText;
|
|
621
|
+
if (ts.isPropertyAccessExpression(call.expression)) {
|
|
622
|
+
const objExpr = call.expression.expression;
|
|
623
|
+
const methodName = escapeCppKeyword(call.expression.name.text);
|
|
624
|
+
if (ts.isIdentifier(objExpr) && pointerVars.has(objExpr.text)) {
|
|
625
|
+
calleeText = `${objExpr.text}->${methodName}`;
|
|
626
|
+
}
|
|
627
|
+
else if (ts.isCallExpression(objExpr) && ts.isPropertyAccessExpression(objExpr.expression)) {
|
|
628
|
+
// Chained method call: obj.method1().method2()
|
|
629
|
+
const innerReceiver = objExpr.expression.expression;
|
|
630
|
+
const innerMethodName = objExpr.expression.name.text;
|
|
631
|
+
const innerCallText = renderExprAsText(expressionToIR(objExpr, sourceText, diagnostics, pointerVars));
|
|
632
|
+
let accessor = ".";
|
|
633
|
+
if (ts.isIdentifier(innerReceiver) && pointerVars.has(innerReceiver.text)) {
|
|
634
|
+
let className = pointerVars.get(innerReceiver.text);
|
|
635
|
+
if (className)
|
|
636
|
+
className = nestedClassAliases.get(className) ?? className;
|
|
637
|
+
const cls = className ? hoistedNestedClasses.find(c => c.name === className) : undefined;
|
|
638
|
+
const method = cls?.methods.find(m => m.name === innerMethodName);
|
|
639
|
+
if (method && parsedIsPointer(method.returnType)) {
|
|
640
|
+
accessor = "->";
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
else if (ts.isIdentifier(innerReceiver) && topLevelClassNames.has(innerReceiver.text)) {
|
|
644
|
+
// Static method call on a top-level or cross-module class returning an instance
|
|
645
|
+
const cls = topLevelClasses.get(innerReceiver.text);
|
|
646
|
+
if (!cls) {
|
|
647
|
+
// Cross-module class: factory methods typically return class instances (pointers)
|
|
648
|
+
accessor = "->";
|
|
649
|
+
}
|
|
650
|
+
else {
|
|
651
|
+
const chainMethod = cls.methods.find(m => m.name === innerMethodName);
|
|
652
|
+
if (chainMethod && (parsedIsPointer(chainMethod.returnType) || chainMethod.returnType === innerReceiver.text)) {
|
|
653
|
+
accessor = "->";
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
calleeText = `${innerCallText}${accessor}${methodName}`;
|
|
658
|
+
}
|
|
659
|
+
else {
|
|
660
|
+
// Use expressionToIR for pointer-aware callee construction.
|
|
661
|
+
// expressionToIR's call handler correctly resolves -> for pointer variables
|
|
662
|
+
// and chained property access (e.g. s->player->printStats), avoiding
|
|
663
|
+
// the dot-only calleeToText fallback.
|
|
664
|
+
const callIR = expressionToIR(call, sourceText, diagnostics, pointerVars);
|
|
665
|
+
if (callIR.kind === "method-call" && typeof callIR.callee === "string") {
|
|
666
|
+
return {
|
|
667
|
+
kind: "call",
|
|
668
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
669
|
+
leadingComments: comments.leadingComments,
|
|
670
|
+
trailingComments: comments.trailingComments,
|
|
671
|
+
callee: callIR.callee,
|
|
672
|
+
args: callIR.args,
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
calleeText = calleeToText(call.expression);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
else {
|
|
679
|
+
calleeText = calleeToText(call.expression);
|
|
680
|
+
}
|
|
681
|
+
// Timer calls with arrow callbacks are intercepted before HAL (above);
|
|
682
|
+
// named-function callbacks fall through here unchanged.
|
|
683
|
+
return {
|
|
684
|
+
kind: "call",
|
|
685
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
686
|
+
leadingComments: comments.leadingComments,
|
|
687
|
+
trailingComments: comments.trailingComments,
|
|
688
|
+
callee: calleeText,
|
|
689
|
+
args: call.arguments.map((arg) => expressionToIR(arg, sourceText, diagnostics, pointerVars)),
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
// ── setInterval/setTimeout arrow-callback hoisting ──────────────────────────
|
|
693
|
+
const TIMER_CALLEES = new Set(["setInterval", "setTimeout", "__tc_setInterval", "__tc_setTimeout"]);
|
|
694
|
+
let timerCallbackCounter = 0;
|
|
695
|
+
/**
|
|
696
|
+
* Hoist an inline arrow/function-expression callback argument of a
|
|
697
|
+
* setInterval/setTimeout call to a named free function, and return the
|
|
698
|
+
* rewritten call with the function name replacing the arrow.
|
|
699
|
+
*
|
|
700
|
+
* Returns null if arg[0] isn't an inline arrow/function (named-function
|
|
701
|
+
* callbacks pass through to the generic path unchanged).
|
|
702
|
+
*
|
|
703
|
+
* Called from the generic fallthrough at the end of callToStatement — NOT
|
|
704
|
+
* as an early return — because top-level timer calls reach the fallthrough
|
|
705
|
+
* path, not the early-interception points.
|
|
706
|
+
*/
|
|
707
|
+
function hoistTimerArrowArg(call, fileName, sourceText, diagnostics, pointerVars, comments) {
|
|
708
|
+
const callbackArg = call.arguments[0];
|
|
709
|
+
if (!callbackArg || !(ts.isArrowFunction(callbackArg) || ts.isFunctionExpression(callbackArg))) {
|
|
710
|
+
return null;
|
|
711
|
+
}
|
|
712
|
+
const fnName = `__tc_timer_cb_${timerCallbackCounter++}`;
|
|
713
|
+
const scope = getCurrentIrTypeScope();
|
|
714
|
+
const localVariableTypes = new Map();
|
|
715
|
+
if (scope) {
|
|
716
|
+
for (const [k, v] of scope.globals)
|
|
717
|
+
localVariableTypes.set(k, v);
|
|
718
|
+
}
|
|
719
|
+
const functionReturnTypes = new Map([...(getContext().activeFunctionReturnTypes ?? new Map())]);
|
|
720
|
+
const bodyStatements = lowerStatementList(ts.isBlock(callbackArg.body) ? callbackArg.body.statements : [], fileName, sourceText, diagnostics, functionReturnTypes, localVariableTypes, fnName, undefined, pointerVars);
|
|
721
|
+
if (!ts.isBlock(callbackArg.body)) {
|
|
722
|
+
const exprText = renderExprAsText(expressionToIR(callbackArg.body, sourceText, diagnostics));
|
|
723
|
+
bodyStatements.push({
|
|
724
|
+
kind: "call",
|
|
725
|
+
sourceSpan: makeSourceSpan(callbackArg.body, fileName, sourceText),
|
|
726
|
+
callee: "__EMIT__",
|
|
727
|
+
args: [{ kind: "string", value: exprText }],
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
hoistedNestedFunctions.push({
|
|
731
|
+
originalName: fnName,
|
|
732
|
+
isAsync: false,
|
|
733
|
+
returnType: "void",
|
|
734
|
+
sourceSpan: makeSourceSpan(callbackArg, fileName, sourceText),
|
|
735
|
+
leadingComments: [],
|
|
736
|
+
trailingComments: [],
|
|
737
|
+
parameters: [],
|
|
738
|
+
statements: bodyStatements,
|
|
739
|
+
});
|
|
740
|
+
// Apply the polyfill helper rename (setInterval → __tc_setInterval) so the
|
|
741
|
+
// emitted call matches the runtime helper the framework provides.
|
|
742
|
+
const rawCallee = ts.isIdentifier(call.expression) ? call.expression.text : calleeToText(call.expression);
|
|
743
|
+
const calleeText = rawCallee === "setInterval" ? "__tc_setInterval"
|
|
744
|
+
: rawCallee === "setTimeout" ? "__tc_setTimeout"
|
|
745
|
+
: rawCallee;
|
|
746
|
+
const remainingArgs = call.arguments.slice(1).map((arg) => expressionToIR(arg, sourceText, diagnostics, pointerVars));
|
|
747
|
+
return {
|
|
748
|
+
kind: "call",
|
|
749
|
+
sourceSpan: makeSourceSpan(call, fileName, sourceText),
|
|
750
|
+
leadingComments: comments.leadingComments,
|
|
751
|
+
trailingComments: comments.trailingComments,
|
|
752
|
+
callee: calleeText,
|
|
753
|
+
args: [{ kind: "identifier", value: fnName }, ...remainingArgs],
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
// ── Helper: lower a callback expression to C++ text ─────────────────────────
|
|
757
|
+
// (Moved to ui-callback-lowering.ts so onToggle and watchPin share one path,
|
|
758
|
+
// including console.* → platform transform which lived in neither copy.)
|