@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,977 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import { classTypeNames, topLevelClasses, discriminatedUnionVariantNames, activeEnumNames } from "./build-ir-state.js";
|
|
3
|
+
import { getCurrentIrTypeScope } from "./symbol-types.js";
|
|
4
|
+
import { parseCppType, renderCppType, bareType, elementOf, } from "../api/shared/cpp-type-ir.js";
|
|
5
|
+
const DIRECT_CPP_TYPE_MAP = new Map([
|
|
6
|
+
["int", "int"],
|
|
7
|
+
["float", "float"],
|
|
8
|
+
["bool", "bool"],
|
|
9
|
+
["string", "std::string"],
|
|
10
|
+
["void", "void"],
|
|
11
|
+
["double", "double"],
|
|
12
|
+
["long", "long"],
|
|
13
|
+
["unsigned", "unsigned int"],
|
|
14
|
+
["uint8_t", "uint8_t"],
|
|
15
|
+
["uint16_t", "uint16_t"],
|
|
16
|
+
["uint32_t", "uint32_t"],
|
|
17
|
+
["int8_t", "int8_t"],
|
|
18
|
+
["int16_t", "int16_t"],
|
|
19
|
+
["int32_t", "int32_t"],
|
|
20
|
+
["size_t", "size_t"],
|
|
21
|
+
["Uint8Array", "uint8_t*"],
|
|
22
|
+
["Uint16Array", "uint16_t*"],
|
|
23
|
+
["Uint32Array", "uint32_t*"],
|
|
24
|
+
["Int8Array", "int8_t*"],
|
|
25
|
+
["Int16Array", "int16_t*"],
|
|
26
|
+
["Int32Array", "int32_t*"],
|
|
27
|
+
["Float32Array", "float*"],
|
|
28
|
+
["Float64Array", "double*"],
|
|
29
|
+
]);
|
|
30
|
+
const PIN_INTERFACE_TYPE_NAMES = new Set([
|
|
31
|
+
"BasePin", "Pin", "PWMPin", "AnalogPin", "InterruptPin",
|
|
32
|
+
"InterruptOptions",
|
|
33
|
+
"PinMode", "InterruptMode",
|
|
34
|
+
]);
|
|
35
|
+
const BUS_INTERFACE_TYPE_NAMES = new Set([
|
|
36
|
+
"II2CBus", "ISPIBus", "ISerialPort", "IUART",
|
|
37
|
+
"I2CConfig", "SPIConfig", "UARTConfig",
|
|
38
|
+
"I2CAddress", "UARTStatus", "SPITransferOptions",
|
|
39
|
+
]);
|
|
40
|
+
const STRATEGY_TYPE_NAMES = new Set([
|
|
41
|
+
"NativeStrategy", "ArduinoStrategy", "BoardStrategy",
|
|
42
|
+
"RuntimePolyfillIR", "PeripheralUsage",
|
|
43
|
+
]);
|
|
44
|
+
const BOARD_CONSTANT_TYPE_NAMES = new Set([
|
|
45
|
+
"INPUT", "OUTPUT", "INPUT_PULLUP", "INPUT_PULLDOWN",
|
|
46
|
+
"CHANGE", "FALLING", "RISING",
|
|
47
|
+
]);
|
|
48
|
+
const OWNERSHIP_WRAPPER_TYPE_NAMES = new Set([
|
|
49
|
+
"Owned", "Shared", "Mutable",
|
|
50
|
+
]);
|
|
51
|
+
export function resolveAliasedTypeNode(node, typeAliases, visited = new Set()) {
|
|
52
|
+
if (!node || !typeAliases) {
|
|
53
|
+
return node;
|
|
54
|
+
}
|
|
55
|
+
if (!ts.isTypeReferenceNode(node) || !ts.isIdentifier(node.typeName)) {
|
|
56
|
+
return node;
|
|
57
|
+
}
|
|
58
|
+
const aliasName = node.typeName.text;
|
|
59
|
+
if (visited.has(aliasName)) {
|
|
60
|
+
return node;
|
|
61
|
+
}
|
|
62
|
+
const aliasNode = typeAliases.get(aliasName);
|
|
63
|
+
if (!aliasNode) {
|
|
64
|
+
return node;
|
|
65
|
+
}
|
|
66
|
+
visited.add(aliasName);
|
|
67
|
+
return resolveAliasedTypeNode(aliasNode, typeAliases, visited);
|
|
68
|
+
}
|
|
69
|
+
function inferNumericCppType(literalText) {
|
|
70
|
+
return /[.eE]/.test(literalText) ? "double" : "int";
|
|
71
|
+
}
|
|
72
|
+
export function getDirectCppType(typeName) {
|
|
73
|
+
const directCppType = DIRECT_CPP_TYPE_MAP.get(typeName);
|
|
74
|
+
return directCppType;
|
|
75
|
+
}
|
|
76
|
+
function isBoardDefinitionTypeName(typeName) {
|
|
77
|
+
return typeName.endsWith("Board") || typeName.endsWith("Definition") ||
|
|
78
|
+
typeName.startsWith("Native");
|
|
79
|
+
}
|
|
80
|
+
function isSerialTypeName(typeName) {
|
|
81
|
+
return typeName === "Serial" || typeName.endsWith("Serial");
|
|
82
|
+
}
|
|
83
|
+
function isPinConstantTypeName(typeName) {
|
|
84
|
+
return /^D\d+$/.test(typeName) ||
|
|
85
|
+
/^A\d+$/.test(typeName) ||
|
|
86
|
+
typeName === "LED" ||
|
|
87
|
+
typeName === "TX" ||
|
|
88
|
+
typeName === "RX" ||
|
|
89
|
+
typeName.startsWith("TX") ||
|
|
90
|
+
typeName.startsWith("RX");
|
|
91
|
+
}
|
|
92
|
+
function isPinImplementationTypeName(typeName) {
|
|
93
|
+
// Framework pin implementation types follow the convention {FRAMEWORK}{Capability}Pin
|
|
94
|
+
// (e.g., AVRDigitalPin, ESP32AnalogPin, STM32PWMPin). Recognized by ALL_CAPS prefix
|
|
95
|
+
// followed by a pin-related suffix, or the Native* prefix for bare-metal strategies.
|
|
96
|
+
return typeName.startsWith("Native") ||
|
|
97
|
+
(/^[A-Z][A-Z0-9]/.test(typeName) && /Pin/.test(typeName));
|
|
98
|
+
}
|
|
99
|
+
function isSharedCompileTimeOnlyTypeName(typeName) {
|
|
100
|
+
return PIN_INTERFACE_TYPE_NAMES.has(typeName) ||
|
|
101
|
+
STRATEGY_TYPE_NAMES.has(typeName) ||
|
|
102
|
+
BOARD_CONSTANT_TYPE_NAMES.has(typeName) ||
|
|
103
|
+
OWNERSHIP_WRAPPER_TYPE_NAMES.has(typeName) ||
|
|
104
|
+
isBoardDefinitionTypeName(typeName) ||
|
|
105
|
+
isSerialTypeName(typeName) ||
|
|
106
|
+
isPinConstantTypeName(typeName) ||
|
|
107
|
+
isPinImplementationTypeName(typeName);
|
|
108
|
+
}
|
|
109
|
+
function isKnownCompileTimeOnlyTypeName(typeName) {
|
|
110
|
+
return BUS_INTERFACE_TYPE_NAMES.has(typeName) || isSharedCompileTimeOnlyTypeName(typeName);
|
|
111
|
+
}
|
|
112
|
+
function normalizeTypeHintForUse(typeHint) {
|
|
113
|
+
if (typeHint === "auto") {
|
|
114
|
+
return "double";
|
|
115
|
+
}
|
|
116
|
+
return typeHint;
|
|
117
|
+
}
|
|
118
|
+
function functionTypeNodeToCppType(node, typeAliases, typeParametersInScope) {
|
|
119
|
+
// Build via CppTypeIR so the function signature is structured, then flatten
|
|
120
|
+
// to the canonical `std::function<R(P...)>` string at the boundary.
|
|
121
|
+
const returnType = normalizeTypeHintForUse(typeNodeToCppType(node.type, typeAliases, typeParametersInScope));
|
|
122
|
+
const params = node.parameters.map((parameter) => normalizeTypeHintForUse(typeNodeToCppType(parameter.type, typeAliases, typeParametersInScope)));
|
|
123
|
+
const ir = {
|
|
124
|
+
kind: "function",
|
|
125
|
+
returnType: parseCppType(returnType),
|
|
126
|
+
params: params.map((p) => parseCppType(p)),
|
|
127
|
+
};
|
|
128
|
+
return renderCppType(ir);
|
|
129
|
+
}
|
|
130
|
+
export function typeNodeToCppType(node, typeAliases, typeParametersInScope) {
|
|
131
|
+
if (!node) {
|
|
132
|
+
return "auto";
|
|
133
|
+
}
|
|
134
|
+
if (ts.isTypeReferenceNode(node) && ts.isIdentifier(node.typeName)) {
|
|
135
|
+
const typeName = node.typeName.text;
|
|
136
|
+
if (typeParametersInScope && typeParametersInScope.has(typeName)) {
|
|
137
|
+
return typeName;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// ── Ownership wrapper detection (before alias resolution) ───────────
|
|
141
|
+
// Shared<T>, Mutable<T>, Owned<T> are phantom types that wrap the real type.
|
|
142
|
+
// We must check the ORIGINAL node before alias resolution, because
|
|
143
|
+
// `type Shared<T> = T` would resolve Shared<number> → number, hiding the wrapper.
|
|
144
|
+
if (ts.isTypeReferenceNode(node) && ts.isIdentifier(node.typeName)) {
|
|
145
|
+
const wrapperName = node.typeName.text;
|
|
146
|
+
if (OWNERSHIP_WRAPPER_TYPE_NAMES.has(wrapperName)) {
|
|
147
|
+
const innerTypeNode = node.typeArguments?.[0];
|
|
148
|
+
return typeNodeToCppType(innerTypeNode, typeAliases, typeParametersInScope);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const resolvedNode = resolveAliasedTypeNode(node, typeAliases) ?? node;
|
|
152
|
+
// If the resolved node is an object literal type (e.g. type X = { a: number }),
|
|
153
|
+
// return the original type reference name as the C++ struct type.
|
|
154
|
+
if (ts.isTypeLiteralNode(resolvedNode) && ts.isTypeReferenceNode(node) && ts.isIdentifier(node.typeName)) {
|
|
155
|
+
return node.typeName.text;
|
|
156
|
+
}
|
|
157
|
+
// Handle mapped types like { readonly [P in keyof SomeType]: SomeType[P] }
|
|
158
|
+
// These are type-level copies; in C++ they're equivalent to the source type.
|
|
159
|
+
// Resolve to the source type name since C++ doesn't have readonly.
|
|
160
|
+
if (ts.isMappedTypeNode(resolvedNode)) {
|
|
161
|
+
const constraint = resolvedNode.typeParameter?.constraint;
|
|
162
|
+
if (constraint && ts.isTypeOperatorNode(constraint) && constraint.operator === ts.SyntaxKind.KeyOfKeyword) {
|
|
163
|
+
const sourceType = constraint.type;
|
|
164
|
+
if (sourceType && ts.isTypeReferenceNode(sourceType) && ts.isIdentifier(sourceType.typeName)) {
|
|
165
|
+
// The mapped type mirrors the source type; resolve to the source type name
|
|
166
|
+
return sourceType.typeName.text;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Generic mapped type fallback: use the outer type reference name
|
|
170
|
+
if (ts.isTypeReferenceNode(node) && ts.isIdentifier(node.typeName)) {
|
|
171
|
+
return node.typeName.text;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (ts.isParenthesizedTypeNode(resolvedNode)) {
|
|
175
|
+
return typeNodeToCppType(resolvedNode.type, typeAliases, typeParametersInScope);
|
|
176
|
+
}
|
|
177
|
+
if (ts.isUnionTypeNode(resolvedNode)) {
|
|
178
|
+
const nonNullTypes = resolvedNode.types.filter(t => {
|
|
179
|
+
return t.kind !== ts.SyntaxKind.NullKeyword && t.kind !== ts.SyntaxKind.UndefinedKeyword;
|
|
180
|
+
});
|
|
181
|
+
if (nonNullTypes.length === 0) {
|
|
182
|
+
return "auto";
|
|
183
|
+
}
|
|
184
|
+
if (nonNullTypes.length === 1) {
|
|
185
|
+
return typeNodeToCppType(nonNullTypes[0], typeAliases, typeParametersInScope);
|
|
186
|
+
}
|
|
187
|
+
const allAreTypeLiterals = nonNullTypes.every(ts.isTypeLiteralNode);
|
|
188
|
+
if (allAreTypeLiterals) {
|
|
189
|
+
const variantNames = discriminatedUnionVariantNames.get(ts.isTypeReferenceNode(node) && ts.isIdentifier(node.typeName) ? node.typeName.text : "");
|
|
190
|
+
if (variantNames) {
|
|
191
|
+
return `std::variant<${variantNames.join(", ")}>`;
|
|
192
|
+
}
|
|
193
|
+
const variantNamesFallback = nonNullTypes.map((_, i) => `_Variant_${i}`);
|
|
194
|
+
return `std::variant<${variantNamesFallback.join(", ")}>`;
|
|
195
|
+
}
|
|
196
|
+
const memberTypes = nonNullTypes
|
|
197
|
+
.map(t => typeNodeToCppType(t, typeAliases, typeParametersInScope))
|
|
198
|
+
.filter((t) => t !== "auto" && t !== undefined);
|
|
199
|
+
const uniqueTypes = [...new Set(memberTypes)];
|
|
200
|
+
if (uniqueTypes.length >= 2) {
|
|
201
|
+
// Build via CppTypeIR.variant so the members are parsed structure, not
|
|
202
|
+
// a hand-joined string. Renders byte-identically to the old form.
|
|
203
|
+
const ir = {
|
|
204
|
+
kind: "variant",
|
|
205
|
+
members: uniqueTypes.map((t) => parseCppType(t)),
|
|
206
|
+
};
|
|
207
|
+
return renderCppType(ir);
|
|
208
|
+
}
|
|
209
|
+
if (uniqueTypes.length === 1) {
|
|
210
|
+
return uniqueTypes[0];
|
|
211
|
+
}
|
|
212
|
+
return "auto";
|
|
213
|
+
}
|
|
214
|
+
if (ts.isArrayTypeNode(resolvedNode)) {
|
|
215
|
+
const elementType = normalizeTypeHintForUse(typeNodeToCppType(resolvedNode.elementType, typeAliases, typeParametersInScope));
|
|
216
|
+
const ir = { kind: "vector", element: parseCppType(elementType) };
|
|
217
|
+
return renderCppType(ir);
|
|
218
|
+
}
|
|
219
|
+
if (ts.isTypeReferenceNode(resolvedNode) && ts.isIdentifier(resolvedNode.typeName) && resolvedNode.typeName.text === "Array") {
|
|
220
|
+
const elementTypeNode = resolvedNode.typeArguments?.[0];
|
|
221
|
+
const elementType = normalizeTypeHintForUse(typeNodeToCppType(elementTypeNode, typeAliases, typeParametersInScope));
|
|
222
|
+
const ir = { kind: "vector", element: parseCppType(elementType) };
|
|
223
|
+
return renderCppType(ir);
|
|
224
|
+
}
|
|
225
|
+
if (ts.isFunctionTypeNode(resolvedNode)) {
|
|
226
|
+
return functionTypeNodeToCppType(resolvedNode, typeAliases, typeParametersInScope);
|
|
227
|
+
}
|
|
228
|
+
if (resolvedNode.kind === ts.SyntaxKind.NumberKeyword) {
|
|
229
|
+
return "double";
|
|
230
|
+
}
|
|
231
|
+
if (resolvedNode.kind === ts.SyntaxKind.BooleanKeyword) {
|
|
232
|
+
return "bool";
|
|
233
|
+
}
|
|
234
|
+
if (resolvedNode.kind === ts.SyntaxKind.StringKeyword) {
|
|
235
|
+
return "std::string";
|
|
236
|
+
}
|
|
237
|
+
if (resolvedNode.kind === ts.SyntaxKind.VoidKeyword) {
|
|
238
|
+
return "void";
|
|
239
|
+
}
|
|
240
|
+
if (ts.isTypeReferenceNode(resolvedNode) && ts.isIdentifier(resolvedNode.typeName)) {
|
|
241
|
+
const typeName = resolvedNode.typeName.text;
|
|
242
|
+
const directCppType = getDirectCppType(typeName);
|
|
243
|
+
if (directCppType) {
|
|
244
|
+
return directCppType;
|
|
245
|
+
}
|
|
246
|
+
if (isSharedCompileTimeOnlyTypeName(typeName)) {
|
|
247
|
+
return "auto";
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (ts.isTypeReferenceNode(resolvedNode) && ts.isIdentifier(resolvedNode.typeName) && resolvedNode.typeName.text === "Set") {
|
|
251
|
+
const elementTypeNode = resolvedNode.typeArguments?.[0];
|
|
252
|
+
const elementType = normalizeTypeHintForUse(typeNodeToCppType(elementTypeNode, typeAliases, typeParametersInScope));
|
|
253
|
+
const ir = { kind: "set", element: parseCppType(elementType) };
|
|
254
|
+
return renderCppType(ir);
|
|
255
|
+
}
|
|
256
|
+
if (ts.isTypeReferenceNode(resolvedNode) && ts.isIdentifier(resolvedNode.typeName) && resolvedNode.typeName.text === "ReadonlySet") {
|
|
257
|
+
const elementTypeNode = resolvedNode.typeArguments?.[0];
|
|
258
|
+
const elementType = normalizeTypeHintForUse(typeNodeToCppType(elementTypeNode, typeAliases, typeParametersInScope));
|
|
259
|
+
const ir = { kind: "set", element: parseCppType(elementType) };
|
|
260
|
+
return renderCppType(ir);
|
|
261
|
+
}
|
|
262
|
+
if (ts.isTypeReferenceNode(resolvedNode) && ts.isIdentifier(resolvedNode.typeName) && resolvedNode.typeName.text === "Map") {
|
|
263
|
+
const keyTypeNode = resolvedNode.typeArguments?.[0];
|
|
264
|
+
const valueTypeNode = resolvedNode.typeArguments?.[1];
|
|
265
|
+
const keyType = normalizeTypeHintForUse(typeNodeToCppType(keyTypeNode, typeAliases, typeParametersInScope));
|
|
266
|
+
const valueType = normalizeTypeHintForUse(typeNodeToCppType(valueTypeNode, typeAliases, typeParametersInScope));
|
|
267
|
+
const ir = { kind: "map", key: parseCppType(keyType), value: parseCppType(valueType) };
|
|
268
|
+
return renderCppType(ir);
|
|
269
|
+
}
|
|
270
|
+
if (ts.isTypeReferenceNode(resolvedNode) && ts.isIdentifier(resolvedNode.typeName) && resolvedNode.typeName.text === "ReadonlyMap") {
|
|
271
|
+
const keyTypeNode = resolvedNode.typeArguments?.[0];
|
|
272
|
+
const valueTypeNode = resolvedNode.typeArguments?.[1];
|
|
273
|
+
const keyType = normalizeTypeHintForUse(typeNodeToCppType(keyTypeNode, typeAliases, typeParametersInScope));
|
|
274
|
+
const valueType = normalizeTypeHintForUse(typeNodeToCppType(valueTypeNode, typeAliases, typeParametersInScope));
|
|
275
|
+
const ir = { kind: "map", key: parseCppType(keyType), value: parseCppType(valueType) };
|
|
276
|
+
return renderCppType(ir);
|
|
277
|
+
}
|
|
278
|
+
if (ts.isTypeReferenceNode(resolvedNode) && ts.isIdentifier(resolvedNode.typeName) && resolvedNode.typeName.text === "Record") {
|
|
279
|
+
const keyTypeNode = resolvedNode.typeArguments?.[0];
|
|
280
|
+
const valueTypeNode = resolvedNode.typeArguments?.[1];
|
|
281
|
+
const keyType = normalizeTypeHintForUse(typeNodeToCppType(keyTypeNode, typeAliases, typeParametersInScope));
|
|
282
|
+
const valueType = normalizeTypeHintForUse(typeNodeToCppType(valueTypeNode, typeAliases, typeParametersInScope));
|
|
283
|
+
const ir = { kind: "map", key: parseCppType(keyType), value: parseCppType(valueType) };
|
|
284
|
+
return renderCppType(ir);
|
|
285
|
+
}
|
|
286
|
+
if (ts.isTypeReferenceNode(resolvedNode) && ts.isIdentifier(resolvedNode.typeName) && resolvedNode.typeName.text === "ReadonlyArray") {
|
|
287
|
+
const elementTypeNode = resolvedNode.typeArguments?.[0];
|
|
288
|
+
const elementType = normalizeTypeHintForUse(typeNodeToCppType(elementTypeNode, typeAliases, typeParametersInScope));
|
|
289
|
+
const ir = { kind: "vector", element: parseCppType(elementType) };
|
|
290
|
+
return renderCppType(ir);
|
|
291
|
+
}
|
|
292
|
+
// Tuple types: [A, B, C] → std::tuple<A, B, C>
|
|
293
|
+
if (ts.isTupleTypeNode(resolvedNode)) {
|
|
294
|
+
const elementTypes = resolvedNode.elements.map((el) => {
|
|
295
|
+
if (ts.isOptionalTypeNode(el)) {
|
|
296
|
+
return normalizeTypeHintForUse(typeNodeToCppType(el.type, typeAliases, typeParametersInScope));
|
|
297
|
+
}
|
|
298
|
+
if (ts.isRestTypeNode(el)) {
|
|
299
|
+
return normalizeTypeHintForUse(typeNodeToCppType(el.type, typeAliases, typeParametersInScope));
|
|
300
|
+
}
|
|
301
|
+
return normalizeTypeHintForUse(typeNodeToCppType(el, typeAliases, typeParametersInScope));
|
|
302
|
+
});
|
|
303
|
+
const ir = { kind: "tuple", elements: elementTypes.map((t) => parseCppType(t)) };
|
|
304
|
+
return renderCppType(ir);
|
|
305
|
+
}
|
|
306
|
+
// User-defined types (interfaces, classes, enums) pass through as their C++ type name.
|
|
307
|
+
if (ts.isTypeReferenceNode(resolvedNode) && ts.isIdentifier(resolvedNode.typeName)) {
|
|
308
|
+
const typeName = resolvedNode.typeName.text;
|
|
309
|
+
if (typeName === "Partial" && resolvedNode.typeArguments?.length === 1) {
|
|
310
|
+
return normalizeTypeHintForUse(typeNodeToCppType(resolvedNode.typeArguments[0], typeAliases, typeParametersInScope));
|
|
311
|
+
}
|
|
312
|
+
if (typeName === "Required" && resolvedNode.typeArguments?.length === 1) {
|
|
313
|
+
return normalizeTypeHintForUse(typeNodeToCppType(resolvedNode.typeArguments[0], typeAliases, typeParametersInScope));
|
|
314
|
+
}
|
|
315
|
+
if (typeName === "NonNullable" && resolvedNode.typeArguments?.length === 1) {
|
|
316
|
+
const inner = typeNodeToCppType(resolvedNode.typeArguments[0], typeAliases, typeParametersInScope);
|
|
317
|
+
return (inner || "auto");
|
|
318
|
+
}
|
|
319
|
+
if (typeName === "Readonly" && resolvedNode.typeArguments?.length === 1) {
|
|
320
|
+
return normalizeTypeHintForUse(typeNodeToCppType(resolvedNode.typeArguments[0], typeAliases, typeParametersInScope));
|
|
321
|
+
}
|
|
322
|
+
if (typeName === "Pick" && resolvedNode.typeArguments?.length === 2) {
|
|
323
|
+
return normalizeTypeHintForUse(typeNodeToCppType(resolvedNode.typeArguments[0], typeAliases, typeParametersInScope));
|
|
324
|
+
}
|
|
325
|
+
if (typeName === "Omit" && resolvedNode.typeArguments?.length === 2) {
|
|
326
|
+
return normalizeTypeHintForUse(typeNodeToCppType(resolvedNode.typeArguments[0], typeAliases, typeParametersInScope));
|
|
327
|
+
}
|
|
328
|
+
if (typeName === "Record" && resolvedNode.typeArguments?.length === 2) {
|
|
329
|
+
const keyType = typeNodeToCppType(resolvedNode.typeArguments[0], typeAliases, typeParametersInScope);
|
|
330
|
+
const valueType = typeNodeToCppType(resolvedNode.typeArguments[1], typeAliases, typeParametersInScope);
|
|
331
|
+
const ir = { kind: "map", key: parseCppType(keyType), value: parseCppType(valueType) };
|
|
332
|
+
return renderCppType(ir);
|
|
333
|
+
}
|
|
334
|
+
if (typeName === "ReturnType" || typeName === "Parameters" || typeName === "ConstructorParameters" || typeName === "InstanceType" || typeName === "Extract" || typeName === "Exclude") {
|
|
335
|
+
return "auto";
|
|
336
|
+
}
|
|
337
|
+
// TypeScript class values are references. Keep that representation consistent
|
|
338
|
+
// across declarations, parameters, returns, fields, and containers so `new X()`
|
|
339
|
+
// is never assigned to a value-typed C++ `X` by accident. Modeled as a
|
|
340
|
+
// `pointer(named(typeName, args?))` so the class-vs-value distinction is
|
|
341
|
+
// structural, not a trailing `*` to be re-parsed.
|
|
342
|
+
if (classTypeNames.has(typeName)) {
|
|
343
|
+
const typeArgs = resolvedNode.typeArguments?.map((arg) => normalizeTypeHintForUse(typeNodeToCppType(arg, typeAliases, typeParametersInScope)));
|
|
344
|
+
const namedIr = {
|
|
345
|
+
kind: "named",
|
|
346
|
+
name: typeName,
|
|
347
|
+
args: typeArgs && typeArgs.length > 0 ? typeArgs.map((t) => parseCppType(t)) : undefined,
|
|
348
|
+
};
|
|
349
|
+
const ir = { kind: "pointer", base: namedIr };
|
|
350
|
+
return renderCppType(ir);
|
|
351
|
+
}
|
|
352
|
+
return resolvedNode.typeName.text;
|
|
353
|
+
}
|
|
354
|
+
if (ts.isIntersectionTypeNode(resolvedNode)) {
|
|
355
|
+
for (const part of resolvedNode.types) {
|
|
356
|
+
const cppType = typeNodeToCppType(part, typeAliases, typeParametersInScope);
|
|
357
|
+
if (cppType && cppType !== "auto" && cppType !== "void") {
|
|
358
|
+
return cppType;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return "auto";
|
|
362
|
+
}
|
|
363
|
+
if (ts.isLiteralTypeNode(resolvedNode)) {
|
|
364
|
+
if (ts.isStringLiteral(resolvedNode.literal))
|
|
365
|
+
return "std::string";
|
|
366
|
+
if (ts.isNumericLiteral(resolvedNode.literal))
|
|
367
|
+
return "int";
|
|
368
|
+
if (resolvedNode.literal.kind === ts.SyntaxKind.TrueKeyword || resolvedNode.literal.kind === ts.SyntaxKind.FalseKeyword)
|
|
369
|
+
return "bool";
|
|
370
|
+
return "auto";
|
|
371
|
+
}
|
|
372
|
+
// Indexed access types: T[K] — resolve to auto for now since C++ doesn't have this
|
|
373
|
+
if (resolvedNode.kind === ts.SyntaxKind.IndexedAccessType) {
|
|
374
|
+
const indexed = resolvedNode;
|
|
375
|
+
const objectType = typeNodeToCppType(indexed.objectType, typeAliases, typeParametersInScope);
|
|
376
|
+
if (objectType && objectType !== "auto") {
|
|
377
|
+
const indexType = indexed.indexType;
|
|
378
|
+
if (ts.isLiteralTypeNode(indexType) && ts.isStringLiteral(indexType.literal)) {
|
|
379
|
+
return "auto";
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return "auto";
|
|
383
|
+
}
|
|
384
|
+
// Conditional types: T extends U ? X : Y — resolve to true branch optimistically
|
|
385
|
+
if (resolvedNode.kind === ts.SyntaxKind.ConditionalType) {
|
|
386
|
+
const conditional = resolvedNode;
|
|
387
|
+
return typeNodeToCppType(conditional.trueType, typeAliases, typeParametersInScope);
|
|
388
|
+
}
|
|
389
|
+
// Template literal types — no C++ equivalent
|
|
390
|
+
if (resolvedNode.kind === ts.SyntaxKind.TemplateLiteralType ||
|
|
391
|
+
resolvedNode.kind === ts.SyntaxKind.TemplateLiteralTypeSpan) {
|
|
392
|
+
return "auto";
|
|
393
|
+
}
|
|
394
|
+
// keyof T — return auto since C++ doesn't have this concept
|
|
395
|
+
if (ts.isTypeOperatorNode(resolvedNode) && resolvedNode.operator === ts.SyntaxKind.KeyOfKeyword) {
|
|
396
|
+
return "auto";
|
|
397
|
+
}
|
|
398
|
+
return "auto";
|
|
399
|
+
}
|
|
400
|
+
export function resolveFunctionTypeSignature(typeNode, typeAliases) {
|
|
401
|
+
const resolvedNode = resolveAliasedTypeNode(typeNode, typeAliases);
|
|
402
|
+
if (!resolvedNode || !ts.isFunctionTypeNode(resolvedNode)) {
|
|
403
|
+
return undefined;
|
|
404
|
+
}
|
|
405
|
+
return {
|
|
406
|
+
parameterTypes: resolvedNode.parameters.map((parameter) => typeNodeToCppType(parameter.type, typeAliases)),
|
|
407
|
+
returnType: typeNodeToCppType(resolvedNode.type, typeAliases),
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
function isStructuredTypeAnnotation(typeNode, typeAliases) {
|
|
411
|
+
const resolvedNode = resolveAliasedTypeNode(typeNode, typeAliases);
|
|
412
|
+
if (!resolvedNode) {
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
if (ts.isTypeLiteralNode(resolvedNode)) {
|
|
416
|
+
return true;
|
|
417
|
+
}
|
|
418
|
+
if (ts.isTypeReferenceNode(resolvedNode) && ts.isIdentifier(resolvedNode.typeName)) {
|
|
419
|
+
return resolvedNode.typeName.text === "Record";
|
|
420
|
+
}
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
423
|
+
function isKnownTypeName(typeName) {
|
|
424
|
+
return isKnownCompileTimeOnlyTypeName(typeName);
|
|
425
|
+
}
|
|
426
|
+
function isKnownCompileTimeType(typeNode, typeAliases) {
|
|
427
|
+
const resolvedNode = resolveAliasedTypeNode(typeNode, typeAliases);
|
|
428
|
+
if (!resolvedNode) {
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
if (ts.isIntersectionTypeNode(resolvedNode)) {
|
|
432
|
+
return resolvedNode.types.every((part) => isKnownCompileTimeType(part, typeAliases));
|
|
433
|
+
}
|
|
434
|
+
if (ts.isUnionTypeNode(resolvedNode)) {
|
|
435
|
+
return resolvedNode.types.every((part) => isKnownCompileTimeType(part, typeAliases));
|
|
436
|
+
}
|
|
437
|
+
if (ts.isParenthesizedTypeNode(resolvedNode)) {
|
|
438
|
+
return isKnownCompileTimeType(resolvedNode.type, typeAliases);
|
|
439
|
+
}
|
|
440
|
+
if (ts.isTypeReferenceNode(resolvedNode) && ts.isIdentifier(resolvedNode.typeName)) {
|
|
441
|
+
return isKnownTypeName(resolvedNode.typeName.text);
|
|
442
|
+
}
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
445
|
+
export function inferExprCppType(expr, functionReturnTypes, localVariableTypes, sourceText) {
|
|
446
|
+
if (ts.isAwaitExpression(expr)) {
|
|
447
|
+
return inferExprCppType(expr.expression, functionReturnTypes, localVariableTypes, sourceText);
|
|
448
|
+
}
|
|
449
|
+
if (ts.isNonNullExpression(expr) || ts.isParenthesizedExpression(expr)) {
|
|
450
|
+
return inferExprCppType(expr.expression, functionReturnTypes, localVariableTypes, sourceText);
|
|
451
|
+
}
|
|
452
|
+
if (ts.isAsExpression(expr) || ts.isTypeAssertionExpression(expr)) {
|
|
453
|
+
return inferExprCppType(expr.expression, functionReturnTypes, localVariableTypes, sourceText);
|
|
454
|
+
}
|
|
455
|
+
// typeof always produces a string result
|
|
456
|
+
if (ts.isTypeOfExpression(expr)) {
|
|
457
|
+
return "std::string";
|
|
458
|
+
}
|
|
459
|
+
if (ts.isNumericLiteral(expr)) {
|
|
460
|
+
// Use original source text — TypeScript normalizes "2.0" to "2" in expr.text
|
|
461
|
+
const literalText = sourceText
|
|
462
|
+
? sourceText.substring(expr.pos, expr.end).trim()
|
|
463
|
+
: expr.text;
|
|
464
|
+
return inferNumericCppType(literalText);
|
|
465
|
+
}
|
|
466
|
+
if (ts.isStringLiteral(expr) || ts.isNoSubstitutionTemplateLiteral(expr)) {
|
|
467
|
+
return "std::string";
|
|
468
|
+
}
|
|
469
|
+
if (ts.isArrayLiteralExpression(expr)) {
|
|
470
|
+
const inferredElementTypes = expr.elements
|
|
471
|
+
.filter((item) => !ts.isSpreadElement(item))
|
|
472
|
+
.map((item) => inferExprCppType(item, functionReturnTypes, localVariableTypes, sourceText))
|
|
473
|
+
.filter((item) => item !== "auto");
|
|
474
|
+
if (inferredElementTypes.length === 0) {
|
|
475
|
+
return "std::vector<int>";
|
|
476
|
+
}
|
|
477
|
+
if (inferredElementTypes.includes("float") || inferredElementTypes.includes("double")) {
|
|
478
|
+
return "std::vector<double>";
|
|
479
|
+
}
|
|
480
|
+
if (inferredElementTypes.includes("std::string")) {
|
|
481
|
+
return "std::vector<std::string>";
|
|
482
|
+
}
|
|
483
|
+
if (inferredElementTypes.includes("bool") && !inferredElementTypes.includes("int")) {
|
|
484
|
+
return "std::vector<bool>";
|
|
485
|
+
}
|
|
486
|
+
if (inferredElementTypes.includes("int") || inferredElementTypes.includes("bool")) {
|
|
487
|
+
return "std::vector<int>";
|
|
488
|
+
}
|
|
489
|
+
const firstElem = inferredElementTypes[0];
|
|
490
|
+
if (firstElem) {
|
|
491
|
+
return `std::vector<${firstElem}>`;
|
|
492
|
+
}
|
|
493
|
+
return "std::vector<int>";
|
|
494
|
+
}
|
|
495
|
+
if (expr.kind === ts.SyntaxKind.TrueKeyword || expr.kind === ts.SyntaxKind.FalseKeyword) {
|
|
496
|
+
return "bool";
|
|
497
|
+
}
|
|
498
|
+
if (ts.isCallExpression(expr)) {
|
|
499
|
+
if (ts.isIdentifier(expr.expression)) {
|
|
500
|
+
const fnReturnType = functionReturnTypes.get(expr.expression.text);
|
|
501
|
+
if (fnReturnType && fnReturnType !== "auto") {
|
|
502
|
+
return fnReturnType;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
if (ts.isPropertyAccessExpression(expr.expression)) {
|
|
506
|
+
if (ts.isIdentifier(expr.expression.expression)) {
|
|
507
|
+
const className = expr.expression.expression.text;
|
|
508
|
+
if (className === "Math") {
|
|
509
|
+
const method = expr.expression.name.text;
|
|
510
|
+
if (["floor", "ceil", "round", "abs", "sqrt", "sin", "cos", "tan", "atan2", "log", "exp", "pow", "fmod"].includes(method)) {
|
|
511
|
+
return "double";
|
|
512
|
+
}
|
|
513
|
+
if (method === "min" || method === "max") {
|
|
514
|
+
const leftType = expr.arguments.length > 0
|
|
515
|
+
? inferExprCppType(expr.arguments[0], functionReturnTypes, localVariableTypes, sourceText)
|
|
516
|
+
: "auto";
|
|
517
|
+
if (leftType !== "auto")
|
|
518
|
+
return leftType;
|
|
519
|
+
return "int";
|
|
520
|
+
}
|
|
521
|
+
if (method === "random")
|
|
522
|
+
return "double";
|
|
523
|
+
}
|
|
524
|
+
const classDef = topLevelClasses.get(className);
|
|
525
|
+
if (classDef) {
|
|
526
|
+
const method = expr.expression.name.text;
|
|
527
|
+
const classMethod = classDef.methods.find((m) => m.name === method);
|
|
528
|
+
if (classMethod) {
|
|
529
|
+
return classMethod.returnType;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
const receiverType = inferExprCppType(expr.expression.expression, functionReturnTypes, localVariableTypes, sourceText);
|
|
534
|
+
// Peel pointer/const off the receiver to recover the bare class name.
|
|
535
|
+
// Uses the structured bareType() rather than ad-hoc slice/replace.
|
|
536
|
+
const receiverIr = parseCppType(receiverType);
|
|
537
|
+
const receiverClassName = (() => {
|
|
538
|
+
const bare = bareType(receiverIr);
|
|
539
|
+
return bare.kind === "named" ? bare.name : renderCppType(bare);
|
|
540
|
+
})();
|
|
541
|
+
const classDef = topLevelClasses.get(receiverClassName);
|
|
542
|
+
if (classDef) {
|
|
543
|
+
const method = expr.expression.name.text;
|
|
544
|
+
const classMethod = classDef.methods.find((m) => m.name === method);
|
|
545
|
+
if (classMethod) {
|
|
546
|
+
return classMethod.returnType;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
if (ts.isIdentifier(expr)) {
|
|
552
|
+
// Demo #31 Finding C — a bare identifier may resolve to a MODULE-SCOPE
|
|
553
|
+
// (top-level const/let) variable, not just a function local. The prior
|
|
554
|
+
// code consulted `localVariableTypes` only, so a `for (const r of ARR)`
|
|
555
|
+
// over a top-level `const ARR: string[]` resolved `ARR` to "auto" (the
|
|
556
|
+
// for-of element-type inference then couldn't see the vector element
|
|
557
|
+
// type, the loop variable carried `auto`, and because a for-of var has
|
|
558
|
+
// no initializer the snprintf specifier picker couldn't recover the real
|
|
559
|
+
// type — defaulting to `%d` for a `std::string`, tripping g++ -Wformat=
|
|
560
|
+
// and producing garbage at runtime). The fix consults the IR type scope's
|
|
561
|
+
// `globals` map (populated for every top-level decl in variables.ts) as a
|
|
562
|
+
// fallback, matching what `resolveReceiverCppType` in array-methods.ts
|
|
563
|
+
// already does for the string/array-method disambiguation. This is the
|
|
564
|
+
// same lookup shape used at line ~747 for member-access receivers.
|
|
565
|
+
const fromLocals = localVariableTypes.get(expr.text);
|
|
566
|
+
if (fromLocals && fromLocals !== "auto")
|
|
567
|
+
return fromLocals;
|
|
568
|
+
const fromGlobals = getCurrentIrTypeScope()?.globals.get(expr.text);
|
|
569
|
+
if (fromGlobals && fromGlobals !== "auto")
|
|
570
|
+
return fromGlobals;
|
|
571
|
+
return fromLocals ?? "auto";
|
|
572
|
+
}
|
|
573
|
+
if (ts.isParenthesizedExpression(expr)) {
|
|
574
|
+
return inferExprCppType(expr.expression, functionReturnTypes, localVariableTypes, sourceText);
|
|
575
|
+
}
|
|
576
|
+
if (ts.isConditionalExpression(expr)) {
|
|
577
|
+
const whenTrueType = inferExprCppType(expr.whenTrue, functionReturnTypes, localVariableTypes, sourceText);
|
|
578
|
+
const whenFalseType = inferExprCppType(expr.whenFalse, functionReturnTypes, localVariableTypes, sourceText);
|
|
579
|
+
if (whenTrueType === "std::string" || whenFalseType === "std::string") {
|
|
580
|
+
return "std::string";
|
|
581
|
+
}
|
|
582
|
+
if (whenTrueType === "float" || whenFalseType === "float" || whenTrueType === "double" || whenFalseType === "double") {
|
|
583
|
+
return "double";
|
|
584
|
+
}
|
|
585
|
+
if ((whenTrueType === "int" || whenTrueType === "bool") && (whenFalseType === "int" || whenFalseType === "bool")) {
|
|
586
|
+
return "int";
|
|
587
|
+
}
|
|
588
|
+
if (whenTrueType === whenFalseType) {
|
|
589
|
+
return whenTrueType;
|
|
590
|
+
}
|
|
591
|
+
return "auto";
|
|
592
|
+
}
|
|
593
|
+
if (ts.isPropertyAccessExpression(expr)) {
|
|
594
|
+
if (expr.name.text === "length") {
|
|
595
|
+
return "int";
|
|
596
|
+
}
|
|
597
|
+
if (ts.isIdentifier(expr.expression)) {
|
|
598
|
+
const objName = expr.expression.text;
|
|
599
|
+
if (activeEnumNames.has(objName)) {
|
|
600
|
+
return objName;
|
|
601
|
+
}
|
|
602
|
+
if (/^[A-Z]/.test(objName)) {
|
|
603
|
+
return "int";
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
if (expr.expression.kind === ts.SyntaxKind.ThisKeyword) {
|
|
607
|
+
const fieldKey = `this->${expr.name.text}`;
|
|
608
|
+
const fieldType = getCurrentIrTypeScope()?.classFields.get(fieldKey);
|
|
609
|
+
if (fieldType && fieldType !== "auto")
|
|
610
|
+
return fieldType;
|
|
611
|
+
}
|
|
612
|
+
if (ts.isIdentifier(expr.expression)) {
|
|
613
|
+
// Prefer the caller-supplied local types (e.g. a lambda's param types)
|
|
614
|
+
// before the module-level scope maps, so a callback body like
|
|
615
|
+
// `(n: Node) => n.capacity` resolves `n` -> Node -> capacity -> double.
|
|
616
|
+
// `scope.locals` is the same Map the caller threaded as
|
|
617
|
+
// localVariableTypes, so the prior activeLocalTypes fallback is subsumed.
|
|
618
|
+
const scope = getCurrentIrTypeScope();
|
|
619
|
+
const objType = localVariableTypes.get(expr.expression.text)
|
|
620
|
+
?? scope?.locals.get(expr.expression.text) ?? scope?.globals.get(expr.expression.text);
|
|
621
|
+
if (objType && objType !== "auto") {
|
|
622
|
+
// Peel pointer to recover the bare class name via the structured IR.
|
|
623
|
+
const className = (() => {
|
|
624
|
+
const bare = bareType(parseCppType(objType));
|
|
625
|
+
return bare.kind === "named" ? bare.name : "";
|
|
626
|
+
})();
|
|
627
|
+
const classDef = topLevelClasses.get(className);
|
|
628
|
+
if (classDef) {
|
|
629
|
+
const field = classDef.fields.find(f => f.name === expr.name.text);
|
|
630
|
+
if (field && field.cppType !== "auto")
|
|
631
|
+
return field.cppType;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
// Handle chained property access: s.player.weaponName
|
|
636
|
+
if (ts.isPropertyAccessExpression(expr.expression)) {
|
|
637
|
+
const receiverType = inferExprCppType(expr.expression, functionReturnTypes, localVariableTypes, sourceText);
|
|
638
|
+
if (receiverType && receiverType !== "auto") {
|
|
639
|
+
const className = (() => {
|
|
640
|
+
const bare = bareType(parseCppType(receiverType));
|
|
641
|
+
return bare.kind === "named" ? bare.name : "";
|
|
642
|
+
})();
|
|
643
|
+
const classDef = topLevelClasses.get(className);
|
|
644
|
+
if (classDef) {
|
|
645
|
+
const field = classDef.fields.find(f => f.name === expr.name.text);
|
|
646
|
+
if (field && field.cppType !== "auto")
|
|
647
|
+
return field.cppType;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
return "auto";
|
|
652
|
+
}
|
|
653
|
+
if (ts.isBinaryExpression(expr)) {
|
|
654
|
+
const operator = expr.operatorToken.kind;
|
|
655
|
+
const leftType = inferExprCppType(expr.left, functionReturnTypes, localVariableTypes, sourceText);
|
|
656
|
+
const rightType = inferExprCppType(expr.right, functionReturnTypes, localVariableTypes, sourceText);
|
|
657
|
+
if (operator === ts.SyntaxKind.EqualsEqualsToken ||
|
|
658
|
+
operator === ts.SyntaxKind.EqualsEqualsEqualsToken ||
|
|
659
|
+
operator === ts.SyntaxKind.ExclamationEqualsToken ||
|
|
660
|
+
operator === ts.SyntaxKind.ExclamationEqualsEqualsToken ||
|
|
661
|
+
operator === ts.SyntaxKind.GreaterThanToken ||
|
|
662
|
+
operator === ts.SyntaxKind.GreaterThanEqualsToken ||
|
|
663
|
+
operator === ts.SyntaxKind.LessThanToken ||
|
|
664
|
+
operator === ts.SyntaxKind.LessThanEqualsToken ||
|
|
665
|
+
operator === ts.SyntaxKind.AmpersandAmpersandToken ||
|
|
666
|
+
operator === ts.SyntaxKind.BarBarToken) {
|
|
667
|
+
return "bool";
|
|
668
|
+
}
|
|
669
|
+
if (operator === ts.SyntaxKind.PlusToken && (leftType === "std::string" || rightType === "std::string")) {
|
|
670
|
+
return "std::string";
|
|
671
|
+
}
|
|
672
|
+
if (leftType === "float" || rightType === "float" || leftType === "double" || rightType === "double") {
|
|
673
|
+
return "double";
|
|
674
|
+
}
|
|
675
|
+
if ((leftType === "int" || leftType === "bool" || leftType === "long long" || leftType === "unsigned long long") && (rightType === "int" || rightType === "bool" || rightType === "long long" || rightType === "unsigned long long")) {
|
|
676
|
+
if (leftType === "long long" || rightType === "long long" || leftType === "unsigned long long" || rightType === "unsigned long long") {
|
|
677
|
+
return "long long";
|
|
678
|
+
}
|
|
679
|
+
return "int";
|
|
680
|
+
}
|
|
681
|
+
// Partial type info: if one operand is auto, use the other for arithmetic ops
|
|
682
|
+
if (leftType === "auto" && rightType !== "auto")
|
|
683
|
+
return rightType;
|
|
684
|
+
if (rightType === "auto" && leftType !== "auto")
|
|
685
|
+
return leftType;
|
|
686
|
+
return "auto";
|
|
687
|
+
}
|
|
688
|
+
if (ts.isCallExpression(expr) && ts.isIdentifier(expr.expression)) {
|
|
689
|
+
return functionReturnTypes.get(expr.expression.text) ?? "auto";
|
|
690
|
+
}
|
|
691
|
+
if (ts.isNewExpression(expr)) {
|
|
692
|
+
if (ts.isIdentifier(expr.expression)) {
|
|
693
|
+
const ctorName = expr.expression.text;
|
|
694
|
+
const directType = getDirectCppType(ctorName);
|
|
695
|
+
if (directType) {
|
|
696
|
+
return directType;
|
|
697
|
+
}
|
|
698
|
+
// Build the class pointer via structured IR: pointer(named(ctorName, args?)).
|
|
699
|
+
const typeArgs = expr.typeArguments?.map((ta) => ta.getText());
|
|
700
|
+
const namedIr = {
|
|
701
|
+
kind: "named",
|
|
702
|
+
name: ctorName,
|
|
703
|
+
args: typeArgs && typeArgs.length > 0 ? typeArgs.map((t) => parseCppType(t)) : undefined,
|
|
704
|
+
};
|
|
705
|
+
const ir = { kind: "pointer", base: namedIr };
|
|
706
|
+
return renderCppType(ir);
|
|
707
|
+
}
|
|
708
|
+
return "auto";
|
|
709
|
+
}
|
|
710
|
+
if (ts.isElementAccessExpression(expr)) {
|
|
711
|
+
const objectType = inferExprCppType(expr.expression, functionReturnTypes, localVariableTypes, sourceText);
|
|
712
|
+
// Typed array pointers → element types
|
|
713
|
+
if (objectType === "float*")
|
|
714
|
+
return "float";
|
|
715
|
+
if (objectType === "uint8_t*")
|
|
716
|
+
return "int";
|
|
717
|
+
if (objectType === "int16_t*")
|
|
718
|
+
return "int";
|
|
719
|
+
if (objectType === "int32_t*")
|
|
720
|
+
return "int";
|
|
721
|
+
const objectIr = parseCppType(objectType);
|
|
722
|
+
// std::tuple<N> element access → extract Nth type
|
|
723
|
+
if (objectIr.kind === "tuple") {
|
|
724
|
+
const argExpr = expr.argumentExpression;
|
|
725
|
+
if (argExpr && ts.isNumericLiteral(argExpr)) {
|
|
726
|
+
const idx = parseInt(argExpr.text, 10);
|
|
727
|
+
if (idx >= 0 && idx < objectIr.elements.length) {
|
|
728
|
+
return renderCppType(objectIr.elements[idx]);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
return "auto";
|
|
732
|
+
}
|
|
733
|
+
// Element type of vector / staticArray / cArray, or value type of map.
|
|
734
|
+
// Replaces the three duplicated inline parsers (vector / __tc_StaticArray /
|
|
735
|
+
// C-array) with one structured lookup. The uint8_t/int8_t → int and
|
|
736
|
+
// float/double → double promotions are preserved for element access.
|
|
737
|
+
const elemIr = elementOf(objectIr);
|
|
738
|
+
if (elemIr) {
|
|
739
|
+
const elemStr = renderCppType(elemIr);
|
|
740
|
+
if (elemStr === "uint8_t" || elemStr === "int8_t")
|
|
741
|
+
return "int";
|
|
742
|
+
if (elemStr === "float" || elemStr === "double")
|
|
743
|
+
return "double";
|
|
744
|
+
return elemStr;
|
|
745
|
+
}
|
|
746
|
+
// Check local variable declarations for typed array constructors
|
|
747
|
+
if (ts.isIdentifier(expr.expression)) {
|
|
748
|
+
const varName = expr.expression.text;
|
|
749
|
+
const varType = localVariableTypes.get(varName);
|
|
750
|
+
if (varType === "float*")
|
|
751
|
+
return "float";
|
|
752
|
+
}
|
|
753
|
+
return "auto";
|
|
754
|
+
}
|
|
755
|
+
// Handle arrow functions and function expressions — infer return type from body
|
|
756
|
+
if (ts.isArrowFunction(expr) || ts.isFunctionExpression(expr)) {
|
|
757
|
+
const body = expr.body;
|
|
758
|
+
if (ts.isBlock(body)) {
|
|
759
|
+
const returnTypes = collectReturns(body)
|
|
760
|
+
.filter((item) => item.expression)
|
|
761
|
+
.map((item) => inferExprCppType(item.expression, functionReturnTypes, localVariableTypes, sourceText))
|
|
762
|
+
.filter((item) => item !== "auto");
|
|
763
|
+
if (returnTypes.includes("float") || returnTypes.includes("double")) {
|
|
764
|
+
return "double";
|
|
765
|
+
}
|
|
766
|
+
if (returnTypes.includes("std::string")) {
|
|
767
|
+
return "std::string";
|
|
768
|
+
}
|
|
769
|
+
if (returnTypes.includes("int") || returnTypes.includes("bool")) {
|
|
770
|
+
return "int";
|
|
771
|
+
}
|
|
772
|
+
if (returnTypes.length > 0) {
|
|
773
|
+
return returnTypes[0];
|
|
774
|
+
}
|
|
775
|
+
return "void";
|
|
776
|
+
}
|
|
777
|
+
// Expression body — infer from the expression
|
|
778
|
+
return inferExprCppType(body, functionReturnTypes, localVariableTypes, sourceText);
|
|
779
|
+
}
|
|
780
|
+
return "auto";
|
|
781
|
+
}
|
|
782
|
+
export function resolveDeclarationType(typeNode, initializer, functionReturnTypes, localVariableTypes, typeAliases, sourceText) {
|
|
783
|
+
const explicitType = typeNodeToCppType(typeNode, typeAliases);
|
|
784
|
+
const inferredType = initializer
|
|
785
|
+
? inferExprCppType(initializer, functionReturnTypes, localVariableTypes, sourceText)
|
|
786
|
+
: "auto";
|
|
787
|
+
let resolvedType;
|
|
788
|
+
if (explicitType === "auto") {
|
|
789
|
+
resolvedType = inferredType;
|
|
790
|
+
}
|
|
791
|
+
else if (explicitType === "int" && (inferredType === "float" || inferredType === "double")) {
|
|
792
|
+
resolvedType = "double";
|
|
793
|
+
}
|
|
794
|
+
else {
|
|
795
|
+
resolvedType = explicitType;
|
|
796
|
+
}
|
|
797
|
+
const canLowerFromStructuredInitializer = isStructuredTypeAnnotation(typeNode, typeAliases) &&
|
|
798
|
+
!!initializer &&
|
|
799
|
+
ts.isObjectLiteralExpression(initializer);
|
|
800
|
+
const isKnownType = isKnownCompileTimeType(typeNode, typeAliases);
|
|
801
|
+
return {
|
|
802
|
+
explicitType,
|
|
803
|
+
inferredType,
|
|
804
|
+
resolvedType,
|
|
805
|
+
canLowerFromStructuredInitializer,
|
|
806
|
+
isKnownCompileTimeType: isKnownType,
|
|
807
|
+
shouldWarnUnmappedType: explicitType === "auto" &&
|
|
808
|
+
inferredType === "auto" &&
|
|
809
|
+
!!typeNode &&
|
|
810
|
+
!canLowerFromStructuredInitializer &&
|
|
811
|
+
!isKnownType,
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
/** Collect local variable types from a function body (single-level scan). */
|
|
815
|
+
function collectLocalVarTypes(body, functionReturnTypes, sourceText) {
|
|
816
|
+
const locals = new Map();
|
|
817
|
+
for (const stmt of body.statements) {
|
|
818
|
+
if (ts.isVariableStatement(stmt)) {
|
|
819
|
+
for (const decl of stmt.declarationList.declarations) {
|
|
820
|
+
if (ts.isIdentifier(decl.name) && decl.initializer) {
|
|
821
|
+
const initType = inferExprCppType(decl.initializer, functionReturnTypes, locals, sourceText);
|
|
822
|
+
if (initType !== "auto") {
|
|
823
|
+
locals.set(decl.name.text, initType);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
return locals;
|
|
830
|
+
}
|
|
831
|
+
export function collectReturns(block) {
|
|
832
|
+
const returns = [];
|
|
833
|
+
const walk = (node) => {
|
|
834
|
+
if (ts.isReturnStatement(node)) {
|
|
835
|
+
returns.push(node);
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
node.forEachChild(walk);
|
|
839
|
+
};
|
|
840
|
+
walk(block);
|
|
841
|
+
return returns;
|
|
842
|
+
}
|
|
843
|
+
export function buildFunctionReturnTypeMap(source) {
|
|
844
|
+
const result = new Map();
|
|
845
|
+
const sourceText = source.text;
|
|
846
|
+
const functions = source.statements.filter(ts.isFunctionDeclaration);
|
|
847
|
+
for (let pass = 0; pass < 3; pass++) {
|
|
848
|
+
for (const fn of functions) {
|
|
849
|
+
if (!fn.name || !fn.body) {
|
|
850
|
+
continue;
|
|
851
|
+
}
|
|
852
|
+
if (fn.type) {
|
|
853
|
+
const annotatedType = typeNodeToCppType(fn.type);
|
|
854
|
+
// For generic functions the annotated return type may reference a type
|
|
855
|
+
// parameter (e.g. `function clamp<T>(...): T`). At a call site we can't
|
|
856
|
+
// resolve `T` to a concrete C++ type, so store "auto" — the caller's
|
|
857
|
+
// declaration will then fall back to `auto`/template deduction rather
|
|
858
|
+
// than emitting a literal `T` (which is not a valid C++ type).
|
|
859
|
+
const typeParamNames = new Set((fn.typeParameters ?? []).map(tp => tp.name.text));
|
|
860
|
+
const resolvedAnnotated = typeParamNames.has(annotatedType) ? "auto" : annotatedType;
|
|
861
|
+
if (resolvedAnnotated !== "auto") {
|
|
862
|
+
// Promote int → float when the function body returns float expressions
|
|
863
|
+
if (resolvedAnnotated === "int") {
|
|
864
|
+
const locals = collectLocalVarTypes(fn.body, result, sourceText);
|
|
865
|
+
const returns = collectReturns(fn.body).filter((item) => item.expression);
|
|
866
|
+
const inferredTypes = returns
|
|
867
|
+
.map((item) => inferExprCppType(item.expression, result, locals, sourceText))
|
|
868
|
+
.filter((item) => item !== "auto");
|
|
869
|
+
if (inferredTypes.includes("float") || inferredTypes.includes("double")) {
|
|
870
|
+
result.set(fn.name.text, "double");
|
|
871
|
+
continue;
|
|
872
|
+
}
|
|
873
|
+
// Promote int → long when returning large enum values
|
|
874
|
+
if (returnsLargeEnumValue(fn.body, returns)) {
|
|
875
|
+
result.set(fn.name.text, "long");
|
|
876
|
+
continue;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
result.set(fn.name.text, resolvedAnnotated);
|
|
880
|
+
continue;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
const locals = collectLocalVarTypes(fn.body, result, sourceText);
|
|
884
|
+
const returns = collectReturns(fn.body).filter((item) => item.expression);
|
|
885
|
+
if (returns.length === 0) {
|
|
886
|
+
continue;
|
|
887
|
+
}
|
|
888
|
+
const inferredTypes = returns
|
|
889
|
+
.map((item) => inferExprCppType(item.expression, result, locals, sourceText))
|
|
890
|
+
.filter((item) => item !== "auto");
|
|
891
|
+
if (inferredTypes.length === 0) {
|
|
892
|
+
continue;
|
|
893
|
+
}
|
|
894
|
+
if (inferredTypes.includes("float") || inferredTypes.includes("double")) {
|
|
895
|
+
result.set(fn.name.text, "double");
|
|
896
|
+
}
|
|
897
|
+
else if (inferredTypes.includes("int")) {
|
|
898
|
+
result.set(fn.name.text, "int");
|
|
899
|
+
}
|
|
900
|
+
else if (inferredTypes.includes("bool")) {
|
|
901
|
+
result.set(fn.name.text, "bool");
|
|
902
|
+
}
|
|
903
|
+
else if (inferredTypes.includes("std::string")) {
|
|
904
|
+
result.set(fn.name.text, "std::string");
|
|
905
|
+
}
|
|
906
|
+
else {
|
|
907
|
+
result.set(fn.name.text, inferredTypes[0]);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
return result;
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Check if a function body contains enum declarations with values outside the
|
|
915
|
+
* 16-bit signed int range (>32767 or <-32768) and any return expression
|
|
916
|
+
* accesses those enums.
|
|
917
|
+
*/
|
|
918
|
+
function returnsLargeEnumValue(body, returns) {
|
|
919
|
+
const largeEnumNames = new Set();
|
|
920
|
+
const walkForEnums = (node) => {
|
|
921
|
+
if (ts.isEnumDeclaration(node) && node.name) {
|
|
922
|
+
for (const member of node.members) {
|
|
923
|
+
let value;
|
|
924
|
+
if (member.initializer && ts.isNumericLiteral(member.initializer)) {
|
|
925
|
+
value = Number(member.initializer.text);
|
|
926
|
+
}
|
|
927
|
+
else if (member.initializer &&
|
|
928
|
+
ts.isPrefixUnaryExpression(member.initializer) &&
|
|
929
|
+
member.initializer.operator === ts.SyntaxKind.MinusToken &&
|
|
930
|
+
ts.isNumericLiteral(member.initializer.operand)) {
|
|
931
|
+
value = -Number(member.initializer.operand.text);
|
|
932
|
+
}
|
|
933
|
+
if (value !== undefined && (value > 32767 || value < -32768)) {
|
|
934
|
+
largeEnumNames.add(node.name.text);
|
|
935
|
+
break;
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
node.forEachChild(walkForEnums);
|
|
940
|
+
};
|
|
941
|
+
walkForEnums(body);
|
|
942
|
+
if (largeEnumNames.size === 0)
|
|
943
|
+
return false;
|
|
944
|
+
for (const ret of returns) {
|
|
945
|
+
if (ret.expression &&
|
|
946
|
+
ts.isPropertyAccessExpression(ret.expression) &&
|
|
947
|
+
ts.isIdentifier(ret.expression.expression) &&
|
|
948
|
+
largeEnumNames.has(ret.expression.expression.text)) {
|
|
949
|
+
return true;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
return false;
|
|
953
|
+
}
|
|
954
|
+
export function resolveFunctionReturnType(name, functionReturnTypes) {
|
|
955
|
+
return functionReturnTypes.get(name) ?? "void";
|
|
956
|
+
}
|
|
957
|
+
/**
|
|
958
|
+
* Extract the ownership kind from a TypeScript type node.
|
|
959
|
+
* Detects Shared<T>, Mutable<T>, and Owned<T> wrapper types.
|
|
960
|
+
* Returns undefined if no ownership wrapper is found.
|
|
961
|
+
*/
|
|
962
|
+
export function extractOwnershipKindFromTypeNode(node, typeAliases) {
|
|
963
|
+
if (!node)
|
|
964
|
+
return undefined;
|
|
965
|
+
// Check the ORIGINAL node before alias resolution.
|
|
966
|
+
// `type Shared<T> = T` would resolve Shared<number> → number, hiding the wrapper.
|
|
967
|
+
if (ts.isTypeReferenceNode(node) && ts.isIdentifier(node.typeName)) {
|
|
968
|
+
const wrapperName = node.typeName.text;
|
|
969
|
+
if (wrapperName === 'Shared')
|
|
970
|
+
return 'shared';
|
|
971
|
+
if (wrapperName === 'Mutable')
|
|
972
|
+
return 'mutable';
|
|
973
|
+
if (wrapperName === 'Owned')
|
|
974
|
+
return 'owned';
|
|
975
|
+
}
|
|
976
|
+
return undefined;
|
|
977
|
+
}
|