@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,1045 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import { extractNodeComments, makeDiagnostic, makeSourceSpan } from "../ast-node-utils.js";
|
|
3
|
+
import { inferExprCppType, resolveDeclarationType, typeNodeToCppType, extractOwnershipKindFromTypeNode } from "../type-resolution.js";
|
|
4
|
+
import { parseCppType, renderCppType, bareType, elementOf, isPointer, isVector, isStringLike, } from "../../api/shared/cpp-type-ir.js";
|
|
5
|
+
import { TYPED_ARRAY_ELEMENT_MAP, activeCArrayVars, activeArrayLiteralVars, activeStringVars, mutableArrayVars, arrayLiteralSizes, filteredArrayLengthVars, halInstances, nestedClassAliases, getContext, activeStringEnumNames, requiredIncludes, } from "../build-ir-state.js";
|
|
6
|
+
import { getCurrentIrTypeScope, setScopeLocalType } from "../symbol-types.js";
|
|
7
|
+
import { renderExprAsText } from "../render-expr.js";
|
|
8
|
+
import { expressionToIR } from "../expression-to-ir.js";
|
|
9
|
+
import { buildInlineForLoop } from "./array-methods.js";
|
|
10
|
+
import { isKnownHALClass, getCtorIncludes, registerFloatVariable, resolveHALReceiver, isHALSingleton, } from "../hal-resolver.js";
|
|
11
|
+
import { resolveHALCallForVarInit } from "./hal-call-resolver.js";
|
|
12
|
+
import { recordSignal } from "./ui-call-resolver.js";
|
|
13
|
+
function replaceHalReadBufferPlaceholder(op, varName) {
|
|
14
|
+
if (op.operation === "i2c.read_buffer" && op.buffer === "__HAL_READ_BUF__") {
|
|
15
|
+
return { ...op, buffer: varName };
|
|
16
|
+
}
|
|
17
|
+
return op;
|
|
18
|
+
}
|
|
19
|
+
export function assignmentOperatorToString(kind) {
|
|
20
|
+
switch (kind) {
|
|
21
|
+
case ts.SyntaxKind.EqualsToken:
|
|
22
|
+
return "=";
|
|
23
|
+
case ts.SyntaxKind.PlusEqualsToken:
|
|
24
|
+
return "+=";
|
|
25
|
+
case ts.SyntaxKind.MinusEqualsToken:
|
|
26
|
+
return "-=";
|
|
27
|
+
case ts.SyntaxKind.AsteriskEqualsToken:
|
|
28
|
+
return "*=";
|
|
29
|
+
case ts.SyntaxKind.SlashEqualsToken:
|
|
30
|
+
return "/=";
|
|
31
|
+
case ts.SyntaxKind.PercentEqualsToken:
|
|
32
|
+
return "%=";
|
|
33
|
+
case ts.SyntaxKind.AmpersandEqualsToken:
|
|
34
|
+
return "&=";
|
|
35
|
+
case ts.SyntaxKind.BarEqualsToken:
|
|
36
|
+
return "|=";
|
|
37
|
+
case ts.SyntaxKind.CaretEqualsToken:
|
|
38
|
+
return "^=";
|
|
39
|
+
case ts.SyntaxKind.LessThanLessThanEqualsToken:
|
|
40
|
+
return "<<=";
|
|
41
|
+
case ts.SyntaxKind.GreaterThanGreaterThanEqualsToken:
|
|
42
|
+
return ">>=";
|
|
43
|
+
default:
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export function updateLocalTypeFromAssignment(target, operator, valueType, localVariableTypes) {
|
|
48
|
+
const currentType = localVariableTypes.get(target) ?? "auto";
|
|
49
|
+
if (operator === "=") {
|
|
50
|
+
localVariableTypes.set(target, valueType);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (valueType === "float" || currentType === "float") {
|
|
54
|
+
localVariableTypes.set(target, "float");
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (valueType === "int" || currentType === "int" || valueType === "bool" || currentType === "bool") {
|
|
58
|
+
localVariableTypes.set(target, "int");
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
localVariableTypes.set(target, currentType);
|
|
62
|
+
}
|
|
63
|
+
export function extractForInKeys(expr) {
|
|
64
|
+
if (ts.isObjectLiteralExpression(expr)) {
|
|
65
|
+
return expr.properties
|
|
66
|
+
.filter(ts.isPropertyAssignment)
|
|
67
|
+
.map(p => (ts.isIdentifier(p.name) ? p.name.text : p.name.getText()));
|
|
68
|
+
}
|
|
69
|
+
if (ts.isIdentifier(expr)) {
|
|
70
|
+
const varName = expr.text;
|
|
71
|
+
let parent = expr.parent;
|
|
72
|
+
while (parent && !ts.isBlock(parent) && !ts.isSourceFile(parent)) {
|
|
73
|
+
parent = parent.parent;
|
|
74
|
+
}
|
|
75
|
+
if (parent && (ts.isBlock(parent) || ts.isSourceFile(parent))) {
|
|
76
|
+
for (const stmt of parent.statements) {
|
|
77
|
+
if (ts.isVariableStatement(stmt)) {
|
|
78
|
+
for (const decl of stmt.declarationList.declarations) {
|
|
79
|
+
if (ts.isIdentifier(decl.name) && decl.name.text === varName && decl.initializer && ts.isObjectLiteralExpression(decl.initializer)) {
|
|
80
|
+
return decl.initializer.properties
|
|
81
|
+
.filter(ts.isPropertyAssignment)
|
|
82
|
+
.map(p => (ts.isIdentifier(p.name) ? p.name.text : p.name.getText()));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
export function variableStatementToIR(statement, fileName, sourceText, diagnostics, functionReturnTypes, localVariableTypes, typeAliases, pointerVars, functionNameForDiagnostics, lowerStatementListCallback) {
|
|
92
|
+
const statementComments = extractNodeComments(statement, sourceText);
|
|
93
|
+
const storage = statement.declarationList.flags & ts.NodeFlags.Const
|
|
94
|
+
? "const"
|
|
95
|
+
: statement.declarationList.flags & ts.NodeFlags.Let
|
|
96
|
+
? "let"
|
|
97
|
+
: "var";
|
|
98
|
+
const lowered = [];
|
|
99
|
+
let commentsAssigned = false;
|
|
100
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
101
|
+
// Handle object destructuring: const { a, b } = obj;
|
|
102
|
+
if (ts.isObjectBindingPattern(declaration.name)) {
|
|
103
|
+
if (!declaration.initializer) {
|
|
104
|
+
diagnostics.push(makeDiagnostic(sourceText, declaration.pos, "Destructured declaration without initializer is unsupported.", "error", "TS2CPP_UNSUPPORTED_DECL"));
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const objExpr = expressionToIR(declaration.initializer, sourceText, diagnostics);
|
|
108
|
+
const objText = renderExprAsText(objExpr);
|
|
109
|
+
const objType = ts.isIdentifier(declaration.initializer)
|
|
110
|
+
? getCurrentIrTypeScope()?.locals.get(declaration.initializer.text) ?? getCurrentIrTypeScope()?.globals.get(declaration.initializer.text)
|
|
111
|
+
: undefined;
|
|
112
|
+
const isPointerAccess = objType ? isPointer(parseCppType(objType)) : false;
|
|
113
|
+
const accessor = isPointerAccess ? "->" : ".";
|
|
114
|
+
for (let i = 0; i < declaration.name.elements.length; i++) {
|
|
115
|
+
const element = declaration.name.elements[i];
|
|
116
|
+
if (!ts.isBindingElement(element)) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (ts.isObjectBindingPattern(element.name)) {
|
|
120
|
+
const nestedPropName = element.propertyName && ts.isIdentifier(element.propertyName)
|
|
121
|
+
? element.propertyName.text
|
|
122
|
+
: undefined;
|
|
123
|
+
if (nestedPropName) {
|
|
124
|
+
const nestedObjText = `${objText}${accessor}${nestedPropName}`;
|
|
125
|
+
// The nested object (e.g. `stats.inner`) is itself a property-access
|
|
126
|
+
// on the root object. Build it as a structured node so the chained
|
|
127
|
+
// access renders correctly and is classified as runtime. See the
|
|
128
|
+
// non-nested case below for the same rationale (demo #6 fix D).
|
|
129
|
+
const nestedObjExpr = {
|
|
130
|
+
kind: "property-access",
|
|
131
|
+
object: objExpr,
|
|
132
|
+
property: nestedPropName,
|
|
133
|
+
isPointer: isPointerAccess,
|
|
134
|
+
};
|
|
135
|
+
for (const nestedElement of element.name.elements) {
|
|
136
|
+
if (!ts.isBindingElement(nestedElement) || !ts.isIdentifier(nestedElement.name))
|
|
137
|
+
continue;
|
|
138
|
+
const nestedVarName = nestedElement.name.text;
|
|
139
|
+
let nPropName = nestedVarName;
|
|
140
|
+
if (nestedElement.propertyName && ts.isIdentifier(nestedElement.propertyName)) {
|
|
141
|
+
nPropName = nestedElement.propertyName.text;
|
|
142
|
+
}
|
|
143
|
+
const propAccess = {
|
|
144
|
+
kind: "property-access",
|
|
145
|
+
object: nestedObjExpr,
|
|
146
|
+
property: nPropName,
|
|
147
|
+
isPointer: false,
|
|
148
|
+
};
|
|
149
|
+
const initializer = nestedElement.initializer
|
|
150
|
+
? { kind: "raw", value: `cuttlefish_nullish(${renderExprAsText(propAccess)}, ${renderExprAsText(expressionToIR(nestedElement.initializer, sourceText, diagnostics))})` }
|
|
151
|
+
: propAccess;
|
|
152
|
+
lowered.push({
|
|
153
|
+
kind: "var_decl",
|
|
154
|
+
sourceSpan: makeSourceSpan(nestedElement, fileName, sourceText),
|
|
155
|
+
leadingComments: [],
|
|
156
|
+
trailingComments: [],
|
|
157
|
+
name: nestedVarName,
|
|
158
|
+
storage,
|
|
159
|
+
cppType: "auto",
|
|
160
|
+
initializer,
|
|
161
|
+
});
|
|
162
|
+
localVariableTypes.set(nestedVarName, "auto");
|
|
163
|
+
commentsAssigned = true;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (!ts.isIdentifier(element.name)) {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
const varName = element.name.text;
|
|
172
|
+
// Get the property name (could be renamed via propertyName)
|
|
173
|
+
let propName;
|
|
174
|
+
if (element.propertyName && ts.isIdentifier(element.propertyName)) {
|
|
175
|
+
propName = element.propertyName.text;
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
propName = varName;
|
|
179
|
+
}
|
|
180
|
+
// Create individual variable declaration for each destructured property.
|
|
181
|
+
// Build a structured `property-access` IR node (NOT a `{kind:"raw"}`
|
|
182
|
+
// string) so that isRuntimeExpression() recognizes the initializer as
|
|
183
|
+
// runtime (a member access on a variable) and routes the declaration
|
|
184
|
+
// into the enclosing function body rather than file scope. A raw
|
|
185
|
+
// "stats.field" string was misclassified as compile-time, so top-level
|
|
186
|
+
// destructuring of a local hoisted the split decls out of main()'s
|
|
187
|
+
// scope ("'stats' was not declared in this scope"). See demo #6 fix D.
|
|
188
|
+
const propAccess = {
|
|
189
|
+
kind: "property-access",
|
|
190
|
+
object: objExpr,
|
|
191
|
+
property: propName,
|
|
192
|
+
isPointer: isPointerAccess,
|
|
193
|
+
};
|
|
194
|
+
const initializer = element.initializer
|
|
195
|
+
? {
|
|
196
|
+
kind: "raw",
|
|
197
|
+
value: `cuttlefish_nullish(${renderExprAsText(propAccess)}, ${renderExprAsText(expressionToIR(element.initializer, sourceText, diagnostics))})`,
|
|
198
|
+
}
|
|
199
|
+
: propAccess;
|
|
200
|
+
lowered.push({
|
|
201
|
+
kind: "var_decl",
|
|
202
|
+
sourceSpan: makeSourceSpan(element, fileName, sourceText),
|
|
203
|
+
leadingComments: i === 0 && !commentsAssigned ? statementComments.leadingComments : [],
|
|
204
|
+
trailingComments: [],
|
|
205
|
+
name: varName,
|
|
206
|
+
storage,
|
|
207
|
+
cppType: "auto",
|
|
208
|
+
initializer,
|
|
209
|
+
});
|
|
210
|
+
localVariableTypes.set(varName, "auto");
|
|
211
|
+
commentsAssigned = true;
|
|
212
|
+
}
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
// Handle array destructuring: const [a, b] = arr;
|
|
216
|
+
if (ts.isArrayBindingPattern(declaration.name)) {
|
|
217
|
+
if (!declaration.initializer) {
|
|
218
|
+
diagnostics.push(makeDiagnostic(sourceText, declaration.pos, "Destructured declaration without initializer is unsupported.", "error", "TS2CPP_UNSUPPORTED_DECL"));
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
const arrExpr = expressionToIR(declaration.initializer, sourceText, diagnostics);
|
|
222
|
+
const arrText = renderExprAsText(arrExpr);
|
|
223
|
+
const isArrayLiteral = arrExpr.kind === "array";
|
|
224
|
+
const arrElements = isArrayLiteral ? arrExpr.elements : null;
|
|
225
|
+
const arrElementType = isArrayLiteral ? arrExpr.elementType : "auto";
|
|
226
|
+
const knownSourceLength = isArrayLiteral
|
|
227
|
+
? arrElements?.length
|
|
228
|
+
: ts.isIdentifier(declaration.initializer)
|
|
229
|
+
? arrayLiteralSizes.get(declaration.initializer.text)
|
|
230
|
+
: undefined;
|
|
231
|
+
for (let i = 0; i < declaration.name.elements.length; i++) {
|
|
232
|
+
const element = declaration.name.elements[i];
|
|
233
|
+
// Skip omitted expressions (holes in array binding pattern)
|
|
234
|
+
if (!ts.isBindingElement(element)) {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
// Handle rest element: const [a, ...rest] = arr;
|
|
238
|
+
if (element.dotDotDotToken && ts.isIdentifier(element.name)) {
|
|
239
|
+
const varName = element.name.text;
|
|
240
|
+
if (isArrayLiteral && arrElements) {
|
|
241
|
+
const remaining = arrElements.slice(i);
|
|
242
|
+
lowered.push({
|
|
243
|
+
kind: "var_decl",
|
|
244
|
+
sourceSpan: makeSourceSpan(element, fileName, sourceText),
|
|
245
|
+
leadingComments: [],
|
|
246
|
+
trailingComments: [],
|
|
247
|
+
name: varName,
|
|
248
|
+
storage,
|
|
249
|
+
cppType: "auto",
|
|
250
|
+
initializer: { kind: "array", elementType: arrElementType, elements: remaining },
|
|
251
|
+
});
|
|
252
|
+
localVariableTypes.set(varName, "auto");
|
|
253
|
+
activeCArrayVars.add(varName);
|
|
254
|
+
commentsAssigned = true;
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
// Derive the element type from the source array so the rest slice
|
|
258
|
+
// lowers to std::vector<ElemType> (NOT std::vector<ElemType&> —
|
|
259
|
+
// `decltype(arr[0])` yields a reference, and vector-of-references
|
|
260
|
+
// is illegal in C++). Fall back to std::remove_reference_t<decltype(...)>
|
|
261
|
+
// when the element type can't be statically resolved.
|
|
262
|
+
//
|
|
263
|
+
// AVR note: a rest element from a VARIABLE lowers to a std::vector
|
|
264
|
+
// slice (no recoverable size from a runtime array), which AVR can't
|
|
265
|
+
// express (no <vector>). Emit a clean, located TS2CPP_NO_VECTOR_STORAGE
|
|
266
|
+
// diagnostic instead of letting it reach g++ as a raw
|
|
267
|
+
// "'vector' is not a member of 'std'" (destructure stress test
|
|
268
|
+
// Finding B). A rest from a LITERAL still works (the literal branch
|
|
269
|
+
// above) because the size is recoverable.
|
|
270
|
+
if (!(getContext().activeStrategy?.needsStdVector() ?? true)) {
|
|
271
|
+
diagnostics.push(makeDiagnostic(sourceText, element.pos, `Rest element \`${varName}\` lowers to std::vector<ElemType> (a runtime slice of \`${arrText}\`), which is not available on this target (ATmega AVR has no <vector>). Use a rest element from an array LITERAL (whose size is recoverable) or index the source array directly.`, "error", "TS2CPP_NO_VECTOR_STORAGE"));
|
|
272
|
+
}
|
|
273
|
+
const srcType = getCurrentIrTypeScope()?.locals.get(arrText) ?? getCurrentIrTypeScope()?.globals.get(arrText);
|
|
274
|
+
let elemType = "auto";
|
|
275
|
+
if (srcType) {
|
|
276
|
+
// One structured lookup replaces the vector/[]/StaticArray branch ladder.
|
|
277
|
+
const elemIr = elementOf(parseCppType(srcType));
|
|
278
|
+
if (elemIr)
|
|
279
|
+
elemType = renderCppType(elemIr);
|
|
280
|
+
}
|
|
281
|
+
const vectorType = elemType !== "auto"
|
|
282
|
+
? `std::vector<${elemType}>`
|
|
283
|
+
: `std::vector<std::remove_reference_t<decltype(${arrText}[0])>>`;
|
|
284
|
+
lowered.push({
|
|
285
|
+
kind: "var_decl",
|
|
286
|
+
sourceSpan: makeSourceSpan(element, fileName, sourceText),
|
|
287
|
+
leadingComments: [],
|
|
288
|
+
trailingComments: [],
|
|
289
|
+
name: varName,
|
|
290
|
+
storage,
|
|
291
|
+
cppType: "auto",
|
|
292
|
+
initializer: { kind: "raw", value: `${vectorType}(${arrText}.begin() + ${i}, ${arrText}.end())` },
|
|
293
|
+
});
|
|
294
|
+
localVariableTypes.set(varName, "auto");
|
|
295
|
+
commentsAssigned = true;
|
|
296
|
+
}
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
if (!ts.isIdentifier(element.name)) {
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
const varName = element.name.text;
|
|
303
|
+
// For array literals, use elements directly; otherwise index into the expression
|
|
304
|
+
let initializer;
|
|
305
|
+
if (isArrayLiteral && arrElements) {
|
|
306
|
+
initializer = arrElements[i] ?? { kind: "raw", value: "CUTTLEFISH_UNDEFINED" };
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
initializer = { kind: "raw", value: `${arrText}[${i}]` };
|
|
310
|
+
}
|
|
311
|
+
if (element.initializer) {
|
|
312
|
+
const defaultValue = expressionToIR(element.initializer, sourceText, diagnostics);
|
|
313
|
+
initializer = knownSourceLength !== undefined && i >= knownSourceLength
|
|
314
|
+
? defaultValue
|
|
315
|
+
: {
|
|
316
|
+
kind: "raw",
|
|
317
|
+
value: `cuttlefish_nullish(${renderExprAsText(initializer)}, ${renderExprAsText(defaultValue)})`,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
lowered.push({
|
|
321
|
+
kind: "var_decl",
|
|
322
|
+
sourceSpan: makeSourceSpan(element, fileName, sourceText),
|
|
323
|
+
leadingComments: i === 0 && !commentsAssigned ? statementComments.leadingComments : [],
|
|
324
|
+
trailingComments: [],
|
|
325
|
+
name: varName,
|
|
326
|
+
storage,
|
|
327
|
+
cppType: "auto",
|
|
328
|
+
initializer,
|
|
329
|
+
});
|
|
330
|
+
localVariableTypes.set(varName, "auto");
|
|
331
|
+
commentsAssigned = true;
|
|
332
|
+
}
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
if (!ts.isIdentifier(declaration.name)) {
|
|
336
|
+
diagnostics.push(makeDiagnostic(sourceText, declaration.pos, "Destructured declarations are currently unsupported.", "error", "TS2CPP_UNSUPPORTED_DECL"));
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
// ── HAL resolver for variable declarations ──────────────────────────────
|
|
340
|
+
if (declaration.initializer && ts.isIdentifier(declaration.name)) {
|
|
341
|
+
const varName = declaration.name.text;
|
|
342
|
+
// ── ui.signal(v): record the signal under the declaration name and emit
|
|
343
|
+
// a plain device variable. The const X = ui.signal(v) binding gives the
|
|
344
|
+
// signal an author-chosen name (preferred over synthesized names).
|
|
345
|
+
if (ts.isCallExpression(declaration.initializer) &&
|
|
346
|
+
ts.isPropertyAccessExpression(declaration.initializer.expression) &&
|
|
347
|
+
ts.isIdentifier(declaration.initializer.expression.expression) &&
|
|
348
|
+
declaration.initializer.expression.expression.text === "ui" &&
|
|
349
|
+
declaration.initializer.expression.name.text === "signal") {
|
|
350
|
+
const valueArg = declaration.initializer.arguments[0];
|
|
351
|
+
let initialValue = 0;
|
|
352
|
+
let cppType = "int";
|
|
353
|
+
if (valueArg) {
|
|
354
|
+
if (ts.isNumericLiteral(valueArg)) {
|
|
355
|
+
initialValue = Number(valueArg.text);
|
|
356
|
+
cppType = Number.isInteger(initialValue) ? "int" : "double";
|
|
357
|
+
}
|
|
358
|
+
else if (ts.isStringLiteral(valueArg)) {
|
|
359
|
+
initialValue = valueArg.text;
|
|
360
|
+
cppType = "const char*";
|
|
361
|
+
}
|
|
362
|
+
else if (valueArg.kind === ts.SyntaxKind.TrueKeyword || valueArg.kind === ts.SyntaxKind.FalseKeyword) {
|
|
363
|
+
initialValue = valueArg.kind === ts.SyntaxKind.TrueKeyword;
|
|
364
|
+
cppType = "bool";
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
recordSignal(varName, cppType, initialValue);
|
|
368
|
+
// Do NOT push a var_decl here — signals are emitted at file scope by
|
|
369
|
+
// uiSignalDecls() so they're global (accessible from binding functions
|
|
370
|
+
// and hoisted timer callbacks). Just register the type for lookups.
|
|
371
|
+
localVariableTypes.set(varName, cppType);
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
if (ts.isNewExpression(declaration.initializer) && ts.isIdentifier(declaration.initializer.expression)) {
|
|
375
|
+
const className = declaration.initializer.expression.text;
|
|
376
|
+
const ctorArgs = declaration.initializer.arguments;
|
|
377
|
+
if (isKnownHALClass(className)) {
|
|
378
|
+
const fieldValues = new Map();
|
|
379
|
+
const ctorIncludes = getCtorIncludes(className);
|
|
380
|
+
for (const inc of ctorIncludes) {
|
|
381
|
+
requiredIncludes.add(inc);
|
|
382
|
+
}
|
|
383
|
+
if (ctorArgs) {
|
|
384
|
+
for (const arg of ctorArgs) {
|
|
385
|
+
if (ts.isIdentifier(arg)) {
|
|
386
|
+
// Resolved pin/port identifier mappings
|
|
387
|
+
}
|
|
388
|
+
else if (ts.isStringLiteral(arg)) {
|
|
389
|
+
if (className === "I2CBus")
|
|
390
|
+
fieldValues.set("_bus", arg.text);
|
|
391
|
+
else if (className === "SPIBus")
|
|
392
|
+
fieldValues.set("_bus", arg.text);
|
|
393
|
+
else if (className === "SerialPort")
|
|
394
|
+
fieldValues.set("_port", arg.text);
|
|
395
|
+
else if (className === "EEPROMClass")
|
|
396
|
+
fieldValues.set("_name", arg.text);
|
|
397
|
+
}
|
|
398
|
+
else if (ts.isNumericLiteral(arg)) {
|
|
399
|
+
if (className === "Pin")
|
|
400
|
+
fieldValues.set("_pin", arg.text);
|
|
401
|
+
}
|
|
402
|
+
else if (ts.isPropertyAccessExpression(arg)) {
|
|
403
|
+
if (className === "Pin")
|
|
404
|
+
fieldValues.set("_pin", arg.getText());
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
if (ctorArgs) {
|
|
409
|
+
for (const arg of ctorArgs) {
|
|
410
|
+
if (ts.isIdentifier(arg) && className === "Pin") {
|
|
411
|
+
const existing = halInstances.get(arg.text);
|
|
412
|
+
if (existing && existing.fieldValues.has("_pin")) {
|
|
413
|
+
fieldValues.set("_pin", existing.fieldValues.get("_pin"));
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
fieldValues.set("_pin", arg.text);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
halInstances.set(varName, { className, fieldValues });
|
|
422
|
+
continue; // skip declaration
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
else if (ts.isCallExpression(declaration.initializer) && ts.isPropertyAccessExpression(declaration.initializer.expression)) {
|
|
426
|
+
const method = declaration.initializer.expression.name.text;
|
|
427
|
+
const result = resolveHALCallForVarInit(declaration.initializer, sourceText, diagnostics, pointerVars);
|
|
428
|
+
const receiver = declaration.initializer.expression.expression;
|
|
429
|
+
const isOwnershipMethod = method === "take" || method === "release" || method === "begin" || method === "end";
|
|
430
|
+
const isSingletonReceiver = ts.isIdentifier(receiver) && isHALSingleton(receiver.text);
|
|
431
|
+
if (result && (!isOwnershipMethod || isSingletonReceiver)) {
|
|
432
|
+
const isHalOpReturn = result.returnValue === "__hal_op_return__";
|
|
433
|
+
if (result.halOps && result.halOps.length > 0) {
|
|
434
|
+
const sideEffectOps = isHalOpReturn ? result.halOps.slice(0, -1) : result.halOps;
|
|
435
|
+
const halStmts = sideEffectOps.map(op => ({
|
|
436
|
+
kind: "hal-op",
|
|
437
|
+
sourceSpan: makeSourceSpan(declaration.initializer, fileName, sourceText),
|
|
438
|
+
operation: replaceHalReadBufferPlaceholder(op, varName),
|
|
439
|
+
returns_value: false,
|
|
440
|
+
}));
|
|
441
|
+
lowered.push(...halStmts);
|
|
442
|
+
commentsAssigned = true;
|
|
443
|
+
}
|
|
444
|
+
const stmts = result.emitLines.map(line => ({
|
|
445
|
+
kind: "call",
|
|
446
|
+
sourceSpan: makeSourceSpan(declaration.initializer, fileName, sourceText),
|
|
447
|
+
callee: "__EMIT__",
|
|
448
|
+
args: [{ kind: "string", value: line.replace(/__HAL_READ_BUF__/g, varName) }],
|
|
449
|
+
}));
|
|
450
|
+
if (stmts.length > 0) {
|
|
451
|
+
lowered.push(...stmts);
|
|
452
|
+
commentsAssigned = true;
|
|
453
|
+
}
|
|
454
|
+
const init = declaration.initializer;
|
|
455
|
+
if (result.returnClassName && ts.isPropertyAccessExpression(init.expression)) {
|
|
456
|
+
const receiver = init.expression.expression;
|
|
457
|
+
const instance = resolveHALReceiver(receiver);
|
|
458
|
+
const fieldValues = new Map(instance?.fieldValues || []);
|
|
459
|
+
// For device() factory calls, copy the address/cs from the call arg
|
|
460
|
+
// so I2CDevice/SPIDevice methods can resolve this._address / this._cs.
|
|
461
|
+
if (result.returnClassName === "I2CDevice" || result.returnClassName === "SPIDevice") {
|
|
462
|
+
const fieldName = result.returnClassName === "SPIDevice" ? "_cs" : "_address";
|
|
463
|
+
const firstArg = init.arguments?.[0];
|
|
464
|
+
if (firstArg) {
|
|
465
|
+
if (ts.isNumericLiteral(firstArg))
|
|
466
|
+
fieldValues.set(fieldName, firstArg.text);
|
|
467
|
+
else if (ts.isStringLiteral(firstArg))
|
|
468
|
+
fieldValues.set(fieldName, firstArg.text);
|
|
469
|
+
else if (ts.isIdentifier(firstArg)) {
|
|
470
|
+
// Resolve pin identifiers (e.g. D10 → 10) via halInstances
|
|
471
|
+
const argInst = resolveHALReceiver(firstArg);
|
|
472
|
+
const pinVal = argInst?.fieldValues.get('_pin') ?? argInst?.fieldValues.get('pin');
|
|
473
|
+
fieldValues.set(fieldName, pinVal ?? firstArg.text);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
halInstances.set(varName, {
|
|
478
|
+
className: result.returnClassName,
|
|
479
|
+
fieldValues,
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
else if (ts.isPropertyAccessExpression(init.expression)) {
|
|
483
|
+
const receiver = init.expression.expression;
|
|
484
|
+
const instance = resolveHALReceiver(receiver);
|
|
485
|
+
// Register the variable as the receiver instance ONLY when the
|
|
486
|
+
// method returns the pin itself ("this", e.g. asOutput/asInput) or
|
|
487
|
+
// returns nothing (a pure side-effect). A value-bearing halOp
|
|
488
|
+
// (readAnalog/readVoltage — returnValue === "__hal_op_return__")
|
|
489
|
+
// must NOT alias the variable to the pin: the variable holds the
|
|
490
|
+
// READ RESULT, and aliasing it makes every later use substitute the
|
|
491
|
+
// pin number (demo #34 Finding B). The value is captured into a
|
|
492
|
+
// real var_decl below (the isHalOpReturn branch).
|
|
493
|
+
if (instance && (!result.returnValue || result.returnValue === "this") && !isHalOpReturn) {
|
|
494
|
+
const aliasInst = {
|
|
495
|
+
className: instance.className,
|
|
496
|
+
fieldValues: new Map(instance.fieldValues),
|
|
497
|
+
...(instance._spreadParamName ? { _spreadParamName: instance._spreadParamName } : {}),
|
|
498
|
+
};
|
|
499
|
+
if (method === "take" && ts.isIdentifier(receiver) && isHALSingleton(receiver.text)) {
|
|
500
|
+
aliasInst.canonicalBusName = receiver.text;
|
|
501
|
+
lowered.push({
|
|
502
|
+
kind: "call",
|
|
503
|
+
sourceSpan: makeSourceSpan(declaration.initializer, fileName, sourceText),
|
|
504
|
+
callee: `${receiver.text}.take`,
|
|
505
|
+
args: [],
|
|
506
|
+
});
|
|
507
|
+
commentsAssigned = true;
|
|
508
|
+
}
|
|
509
|
+
halInstances.set(varName, aliasInst);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
if (result.returnValue && result.returnValue !== "this") {
|
|
513
|
+
if (isHalOpReturn) {
|
|
514
|
+
const lastOp = result.halOps[result.halOps.length - 1];
|
|
515
|
+
lowered.push({
|
|
516
|
+
kind: "var_decl",
|
|
517
|
+
sourceSpan: makeSourceSpan(declaration, fileName, sourceText),
|
|
518
|
+
leadingComments: commentsAssigned ? [] : statementComments.leadingComments,
|
|
519
|
+
trailingComments: [],
|
|
520
|
+
name: varName,
|
|
521
|
+
storage,
|
|
522
|
+
cppType: "auto",
|
|
523
|
+
initializer: { kind: "hal-expr", operation: lastOp },
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
else {
|
|
527
|
+
if (/\b\d+\.\d+\b/.test(result.returnValue)) {
|
|
528
|
+
registerFloatVariable(varName);
|
|
529
|
+
}
|
|
530
|
+
const isTypedArray = result.returnValue.startsWith("__TYPED_ARRAY__:");
|
|
531
|
+
if (isTypedArray) {
|
|
532
|
+
const parts = result.returnValue.split(":");
|
|
533
|
+
const elementType = parts[1];
|
|
534
|
+
const size = parts[2];
|
|
535
|
+
lowered.push({
|
|
536
|
+
kind: "var_decl",
|
|
537
|
+
sourceSpan: makeSourceSpan(declaration, fileName, sourceText),
|
|
538
|
+
leadingComments: commentsAssigned ? [] : statementComments.leadingComments,
|
|
539
|
+
trailingComments: [],
|
|
540
|
+
name: varName,
|
|
541
|
+
storage,
|
|
542
|
+
cppType: `${elementType}[${size}]`,
|
|
543
|
+
initializer: undefined,
|
|
544
|
+
});
|
|
545
|
+
activeCArrayVars.add(varName);
|
|
546
|
+
}
|
|
547
|
+
else {
|
|
548
|
+
lowered.push({
|
|
549
|
+
kind: "var_decl",
|
|
550
|
+
sourceSpan: makeSourceSpan(declaration, fileName, sourceText),
|
|
551
|
+
leadingComments: commentsAssigned ? [] : statementComments.leadingComments,
|
|
552
|
+
trailingComments: [],
|
|
553
|
+
name: varName,
|
|
554
|
+
storage,
|
|
555
|
+
cppType: "auto",
|
|
556
|
+
initializer: { kind: "raw", value: result.returnValue },
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
localVariableTypes.set(varName, "auto");
|
|
561
|
+
commentsAssigned = true;
|
|
562
|
+
}
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
if (declaration.initializer && ts.isIdentifier(declaration.initializer)) {
|
|
567
|
+
const existing = halInstances.get(declaration.initializer.text);
|
|
568
|
+
if (existing) {
|
|
569
|
+
halInstances.set(varName, existing);
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
let isVolatile = false;
|
|
575
|
+
let actualInitializer = declaration.initializer;
|
|
576
|
+
if (declaration.initializer && ts.isCallExpression(declaration.initializer)) {
|
|
577
|
+
const callee = declaration.initializer.expression;
|
|
578
|
+
if (ts.isIdentifier(callee) && callee.text === "volatile") {
|
|
579
|
+
isVolatile = true;
|
|
580
|
+
if (declaration.initializer.arguments.length > 0) {
|
|
581
|
+
actualInitializer = declaration.initializer.arguments[0];
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
actualInitializer = undefined;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
if (actualInitializer && ts.isNewExpression(actualInitializer)) {
|
|
589
|
+
const ctorText = actualInitializer.expression && ts.isIdentifier(actualInitializer.expression)
|
|
590
|
+
? actualInitializer.expression.text : "";
|
|
591
|
+
if (TYPED_ARRAY_ELEMENT_MAP[ctorText] && ts.isIdentifier(declaration.name)) {
|
|
592
|
+
activeCArrayVars.add(declaration.name.text);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
let lambdaInitializer;
|
|
596
|
+
if (actualInitializer && (ts.isArrowFunction(actualInitializer) || ts.isFunctionExpression(actualInitializer))) {
|
|
597
|
+
const fnExpr = actualInitializer;
|
|
598
|
+
const params = [];
|
|
599
|
+
for (const param of fnExpr.parameters) {
|
|
600
|
+
if (ts.isIdentifier(param.name)) {
|
|
601
|
+
const paramType = typeNodeToCppType(param.type, typeAliases);
|
|
602
|
+
params.push({
|
|
603
|
+
name: param.name.text,
|
|
604
|
+
cppType: (paramType === "void" ? "auto" : paramType),
|
|
605
|
+
defaultValue: param.initializer ? expressionToIR(param.initializer, sourceText, diagnostics, pointerVars) : undefined,
|
|
606
|
+
isRest: !!param.dotDotDotToken,
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
const isBlock = ts.isBlock(fnExpr.body);
|
|
611
|
+
const body = isBlock
|
|
612
|
+
? lowerStatementListCallback(fnExpr.body.statements, fileName, sourceText, diagnostics, new Map(), new Map(), declaration.name.getText(), typeAliases, pointerVars)
|
|
613
|
+
: [{
|
|
614
|
+
kind: "return",
|
|
615
|
+
sourceSpan: makeSourceSpan(fnExpr.body, fileName, sourceText),
|
|
616
|
+
value: expressionToIR(fnExpr.body, sourceText, diagnostics, pointerVars),
|
|
617
|
+
}];
|
|
618
|
+
const returnType = typeNodeToCppType(fnExpr.type, typeAliases);
|
|
619
|
+
lambdaInitializer = { kind: "lambda", params, body, returnType, isExpressionBody: !isBlock };
|
|
620
|
+
}
|
|
621
|
+
const loweredDeclaration = {
|
|
622
|
+
kind: "var_decl",
|
|
623
|
+
sourceSpan: makeSourceSpan(declaration, fileName, sourceText),
|
|
624
|
+
leadingComments: commentsAssigned ? [] : statementComments.leadingComments,
|
|
625
|
+
trailingComments: commentsAssigned ? [] : statementComments.trailingComments,
|
|
626
|
+
name: declaration.name.text,
|
|
627
|
+
storage,
|
|
628
|
+
cppType: "auto",
|
|
629
|
+
isVolatile,
|
|
630
|
+
initializer: lambdaInitializer ?? (actualInitializer
|
|
631
|
+
? expressionToIR(actualInitializer, sourceText, diagnostics, pointerVars)
|
|
632
|
+
: undefined),
|
|
633
|
+
};
|
|
634
|
+
commentsAssigned = true;
|
|
635
|
+
const declarationType = resolveDeclarationType(declaration.type, declaration.initializer, functionReturnTypes, localVariableTypes, typeAliases, sourceText);
|
|
636
|
+
let varCppType = declarationType.resolvedType === "void" ? "auto" : declarationType.resolvedType;
|
|
637
|
+
// A string enum lowers to a C++ namespace (not a type), so a variable
|
|
638
|
+
// whose declared TS type is a string enum must use `const char*` as its
|
|
639
|
+
// C++ type — the actual type of a string-enum member. Without this,
|
|
640
|
+
// `const currentColor: Color = Color.Green` emits `Color currentColor = {}`
|
|
641
|
+
// where `Color` is a namespace name, not a valid C++ type (enum stress
|
|
642
|
+
// test Finding A).
|
|
643
|
+
if (activeStringEnumNames.has(varCppType)) {
|
|
644
|
+
varCppType = "const char*";
|
|
645
|
+
}
|
|
646
|
+
// Resolve the base type (with pointer/const stripped) via structured IR so
|
|
647
|
+
// the nestedClassAliases lookup compares against the bare name.
|
|
648
|
+
{
|
|
649
|
+
const varIr = parseCppType(varCppType);
|
|
650
|
+
const ptr = isPointer(varIr);
|
|
651
|
+
const bare = bareType(varIr);
|
|
652
|
+
const bareName = bare.kind === "named" ? bare.name : renderCppType(bare);
|
|
653
|
+
if (nestedClassAliases.has(bareName)) {
|
|
654
|
+
const aliased = nestedClassAliases.get(bareName);
|
|
655
|
+
const aliasedIr = ptr
|
|
656
|
+
? { kind: "pointer", base: parseCppType(aliased) }
|
|
657
|
+
: parseCppType(aliased);
|
|
658
|
+
varCppType = renderCppType(aliasedIr);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
// Object literals without a type annotation get "auto" from type resolution,
|
|
662
|
+
// but the emitter will generate a struct _name_t. Set the cppType early so
|
|
663
|
+
// that string-concat rendering (shouldSkipStringWrap) can recognise string
|
|
664
|
+
// fields by looking up the struct type in interfaceFieldTypes.
|
|
665
|
+
if (varCppType === "auto" && actualInitializer && ts.isObjectLiteralExpression(actualInitializer)) {
|
|
666
|
+
varCppType = `_${declaration.name.text}_t`;
|
|
667
|
+
}
|
|
668
|
+
if (actualInitializer && ts.isArrayLiteralExpression(actualInitializer)) {
|
|
669
|
+
const hasObjectElements = actualInitializer.elements.some((e) => !ts.isSpreadElement(e) && ts.isObjectLiteralExpression(e));
|
|
670
|
+
if (hasObjectElements) {
|
|
671
|
+
// Only generate a shadow struct (_name_t) when the declared type isn't
|
|
672
|
+
// already a named-type vector (e.g. `const pts: Point[]` resolves to
|
|
673
|
+
// std::vector<Point> — use Point directly, don't override with a shadow
|
|
674
|
+
// struct that collides at multiple sites — demo #11 Finding D).
|
|
675
|
+
const varIr = parseCppType(varCppType);
|
|
676
|
+
const elemIr = elementOf(varIr);
|
|
677
|
+
const elemType = elemIr ? renderCppType(elemIr) : null;
|
|
678
|
+
const isNamedElementType = elemType ? /^[A-Z]/.test(elemType) : false;
|
|
679
|
+
if (!isNamedElementType) {
|
|
680
|
+
const structType = `_${declaration.name.text}_t`;
|
|
681
|
+
const ir = { kind: "vector", element: parseCppType(structType) };
|
|
682
|
+
varCppType = renderCppType(ir);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
loweredDeclaration.cppType = varCppType;
|
|
687
|
+
localVariableTypes.set(declaration.name.text, varCppType);
|
|
688
|
+
// Mirror into the scope's locals view so getCurrentIrTypeScope().locals
|
|
689
|
+
// readers (in expression-to-ir) see this binding. localVariableTypes is the
|
|
690
|
+
// accumulating threaded map; scope.locals is the function-resettable view —
|
|
691
|
+
// both must be written because a nested lowerStatementList reset clears
|
|
692
|
+
// scope.locals (not localVariableTypes) and re-syncs from it.
|
|
693
|
+
setScopeLocalType(declaration.name.text, varCppType);
|
|
694
|
+
// Top-level (module-scope) declarations also go into the file-scoped globals
|
|
695
|
+
// map so they resolve from any function in the file.
|
|
696
|
+
if (!functionNameForDiagnostics || functionNameForDiagnostics === "") {
|
|
697
|
+
getCurrentIrTypeScope()?.globals.set(declaration.name.text, varCppType);
|
|
698
|
+
}
|
|
699
|
+
// String-var detection via structured isStringLike rather than a string-equality ladder.
|
|
700
|
+
if (isStringLike(parseCppType(declarationType.resolvedType))) {
|
|
701
|
+
activeStringVars.add(declaration.name.text);
|
|
702
|
+
}
|
|
703
|
+
if (ts.isIdentifier(declaration.name) && actualInitializer) {
|
|
704
|
+
const varName = declaration.name.text;
|
|
705
|
+
if (ts.isArrayLiteralExpression(actualInitializer)
|
|
706
|
+
&& (getContext().activeStrategy?.promotesArrayLiteralsToStaticArray?.() ?? true)) {
|
|
707
|
+
const elements = actualInitializer.elements;
|
|
708
|
+
let elemType = "int";
|
|
709
|
+
// Extract element type via structured elementOf. Prefer `varCppType`
|
|
710
|
+
// (the corrected declared type — for an anonymous-object array literal
|
|
711
|
+
// it carries the shadow struct `_<name>_t`, set above) over the raw
|
|
712
|
+
// resolver view (`declarationType.resolvedType`), which can mis-infer
|
|
713
|
+
// an anonymous object element as `double`.
|
|
714
|
+
const resolvedIr = parseCppType(varCppType && varCppType !== "auto" ? varCppType : declarationType.resolvedType);
|
|
715
|
+
const elemIr = elementOf(resolvedIr);
|
|
716
|
+
if (elemIr) {
|
|
717
|
+
elemType = renderCppType(elemIr);
|
|
718
|
+
}
|
|
719
|
+
else if (declarationType.resolvedType === "auto" && elements.length > 0) {
|
|
720
|
+
elemType = inferExprCppType(elements[0], functionReturnTypes, localVariableTypes, sourceText);
|
|
721
|
+
}
|
|
722
|
+
// Promote to StaticArray when the array is mutated (the original
|
|
723
|
+
// condition) OR when the element type is a struct/non-primitive: a
|
|
724
|
+
// read-only primitive array already emits a C array correctly, but a
|
|
725
|
+
// read-only STRUCT-element array otherwise lowers to std::vector<T>
|
|
726
|
+
// and SILENTLY miscompiles on AVR (no <vector>, no diagnostic) —
|
|
727
|
+
// destructuring stress test Finding D. A named (capitalized) element
|
|
728
|
+
// type is the struct case, as is an anonymous-object shadow struct
|
|
729
|
+
// (`_<name>_t`, generated for inline `{ x, y }` element types);
|
|
730
|
+
// primitives (int32_t, bool, ...) take the existing C-array path.
|
|
731
|
+
const isStructElement = (/^[A-Z]/.test(elemType) && elemType !== "Int")
|
|
732
|
+
|| /^_[A-Za-z0-9_]+_t$/.test(elemType)
|
|
733
|
+
// Anonymous-object array literal: the type resolver may mis-infer
|
|
734
|
+
// the element type (e.g. {x,y}[] → std::vector<double>), so also
|
|
735
|
+
// detect struct elements structurally — any element that is an
|
|
736
|
+
// object literal means a struct-element array (shadow struct).
|
|
737
|
+
|| actualInitializer.elements.some((e) => !ts.isSpreadElement(e) && ts.isObjectLiteralExpression(e));
|
|
738
|
+
const shouldPromote = mutableArrayVars.has(varName) || isStructElement;
|
|
739
|
+
if (shouldPromote) {
|
|
740
|
+
// When the element type is a shadow struct (anonymous object), emit
|
|
741
|
+
// the struct definition BEFORE the StaticArray var_decl. The
|
|
742
|
+
// non-promotion path emits it via the object-literal initializer;
|
|
743
|
+
// the promotion path replaces that initializer, so the definition
|
|
744
|
+
// must be emitted here explicitly.
|
|
745
|
+
if (/^_[A-Za-z0-9_]+_t$/.test(elemType)) {
|
|
746
|
+
const firstObj = elements.find((e) => !ts.isSpreadElement(e) && ts.isObjectLiteralExpression(e));
|
|
747
|
+
if (firstObj && ts.isObjectLiteralExpression(firstObj)) {
|
|
748
|
+
const fieldDefs = firstObj.properties
|
|
749
|
+
.filter(ts.isPropertyAssignment)
|
|
750
|
+
.map((p) => {
|
|
751
|
+
const fname = ts.isIdentifier(p.name) ? p.name.text : p.name.getText();
|
|
752
|
+
const ftype = inferExprCppType(p.initializer, functionReturnTypes, localVariableTypes, sourceText);
|
|
753
|
+
return `${ftype || "auto"} ${fname};`;
|
|
754
|
+
})
|
|
755
|
+
.join(" ");
|
|
756
|
+
lowered.push({
|
|
757
|
+
kind: "call",
|
|
758
|
+
sourceSpan: loweredDeclaration.sourceSpan,
|
|
759
|
+
callee: "__EMIT__",
|
|
760
|
+
args: [{ kind: "string", value: `struct ${elemType} { ${fieldDefs} };` }],
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
const capacity = elements.length + 2;
|
|
765
|
+
const staticArrayIr = {
|
|
766
|
+
kind: "staticArray",
|
|
767
|
+
element: parseCppType(elemType),
|
|
768
|
+
size: capacity,
|
|
769
|
+
};
|
|
770
|
+
lowered.push({
|
|
771
|
+
kind: "var_decl",
|
|
772
|
+
sourceSpan: loweredDeclaration.sourceSpan,
|
|
773
|
+
leadingComments: loweredDeclaration.leadingComments,
|
|
774
|
+
trailingComments: [],
|
|
775
|
+
name: varName,
|
|
776
|
+
storage: "let",
|
|
777
|
+
cppType: renderCppType(staticArrayIr),
|
|
778
|
+
initializer: undefined,
|
|
779
|
+
});
|
|
780
|
+
for (let ei = 0; ei < elements.length; ei++) {
|
|
781
|
+
lowered.push({
|
|
782
|
+
kind: "call",
|
|
783
|
+
sourceSpan: loweredDeclaration.sourceSpan,
|
|
784
|
+
callee: `${varName}.push`,
|
|
785
|
+
args: [expressionToIR(elements[ei], sourceText, diagnostics)],
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
commentsAssigned = true;
|
|
789
|
+
continue;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
// Native target lowers array literals to std::vector (not StaticArray), so
|
|
793
|
+
// the promotion block above is skipped — but a `const` array mutated via
|
|
794
|
+
// .push/.pop/[i]= must STILL be demoted to non-const storage, or the
|
|
795
|
+
// emitted `const std::vector` rejects push_back/operator[]. This mirrors
|
|
796
|
+
// the `storage: "let"` the promotion block sets for StaticArray.
|
|
797
|
+
if (mutableArrayVars.has(varName) && ts.isArrayLiteralExpression(actualInitializer)
|
|
798
|
+
&& !(getContext().activeStrategy?.promotesArrayLiteralsToStaticArray?.() ?? true)) {
|
|
799
|
+
loweredDeclaration.storage = "let";
|
|
800
|
+
}
|
|
801
|
+
if (mutableArrayVars.has(varName) && !ts.isArrayLiteralExpression(actualInitializer)) {
|
|
802
|
+
loweredDeclaration.storage = "let";
|
|
803
|
+
}
|
|
804
|
+
if (ts.isCallExpression(actualInitializer) &&
|
|
805
|
+
ts.isPropertyAccessExpression(actualInitializer.expression) &&
|
|
806
|
+
actualInitializer.expression.name.text === "map" &&
|
|
807
|
+
ts.isIdentifier(actualInitializer.expression.expression)) {
|
|
808
|
+
const srcName = actualInitializer.expression.expression.text;
|
|
809
|
+
const srcSize = arrayLiteralSizes.get(srcName);
|
|
810
|
+
const arrowFn = actualInitializer.arguments[0];
|
|
811
|
+
if (srcSize !== undefined && arrowFn && (ts.isArrowFunction(arrowFn) || ts.isFunctionExpression(arrowFn))) {
|
|
812
|
+
const param = arrowFn.parameters[0];
|
|
813
|
+
const paramName = param && ts.isIdentifier(param.name) ? param.name.text : "__x";
|
|
814
|
+
const bodyExpr = ts.isBlock(arrowFn.body) ? undefined : arrowFn.body;
|
|
815
|
+
if (bodyExpr) {
|
|
816
|
+
const span = loweredDeclaration.sourceSpan;
|
|
817
|
+
const srcType = getCurrentIrTypeScope()?.locals.get(srcName) ?? getCurrentIrTypeScope()?.globals.get(srcName) ?? "auto";
|
|
818
|
+
// Element type of a vector/staticArray/cArray, else the type itself.
|
|
819
|
+
const elemType = (() => {
|
|
820
|
+
const e = elementOf(parseCppType(srcType));
|
|
821
|
+
return e ? renderCppType(e) : srcType;
|
|
822
|
+
})();
|
|
823
|
+
const zeroElements = [];
|
|
824
|
+
for (let zi = 0; zi < srcSize; zi++)
|
|
825
|
+
zeroElements.push({ kind: "number", value: 0 });
|
|
826
|
+
lowered.push({
|
|
827
|
+
kind: "var_decl",
|
|
828
|
+
sourceSpan: span,
|
|
829
|
+
leadingComments: loweredDeclaration.leadingComments,
|
|
830
|
+
trailingComments: [],
|
|
831
|
+
name: varName,
|
|
832
|
+
storage: "let",
|
|
833
|
+
cppType: "auto",
|
|
834
|
+
initializer: { kind: "array", elementType: elemType, elements: zeroElements },
|
|
835
|
+
});
|
|
836
|
+
activeCArrayVars.add(varName);
|
|
837
|
+
lowered.push(buildInlineForLoop(span, srcSize, srcName, paramName, bodyExpr, sourceText, diagnostics, `${varName}[__tc_i]`, false));
|
|
838
|
+
commentsAssigned = true;
|
|
839
|
+
continue;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
if (ts.isCallExpression(actualInitializer) &&
|
|
844
|
+
ts.isPropertyAccessExpression(actualInitializer.expression) &&
|
|
845
|
+
actualInitializer.expression.name.text === "filter" &&
|
|
846
|
+
ts.isIdentifier(actualInitializer.expression.expression)) {
|
|
847
|
+
const srcName = actualInitializer.expression.expression.text;
|
|
848
|
+
const srcSize = arrayLiteralSizes.get(srcName);
|
|
849
|
+
const arrowFn = actualInitializer.arguments[0];
|
|
850
|
+
// Only inline-lower filter when the source has a known, non-zero
|
|
851
|
+
// literal size. A source initialised as `[]` (size 0) or grown via
|
|
852
|
+
// .push() is runtime-sized: the inline lowering would emit a
|
|
853
|
+
// zero-length fixed C array and a `for (... < 0; ...)` loop that
|
|
854
|
+
// never runs. Fall through to the strategy's `__tc_filter` polyfill,
|
|
855
|
+
// which returns a std::vector<T> built up with push_back.
|
|
856
|
+
if (srcSize !== undefined && srcSize > 0 && arrowFn && (ts.isArrowFunction(arrowFn) || ts.isFunctionExpression(arrowFn))) {
|
|
857
|
+
const param = arrowFn.parameters[0];
|
|
858
|
+
const paramName = param && ts.isIdentifier(param.name) ? param.name.text : "__x";
|
|
859
|
+
const bodyExpr = ts.isBlock(arrowFn.body) ? undefined : arrowFn.body;
|
|
860
|
+
if (bodyExpr) {
|
|
861
|
+
const span = loweredDeclaration.sourceSpan;
|
|
862
|
+
const lenVar = `${varName}__len`;
|
|
863
|
+
const srcType = getCurrentIrTypeScope()?.locals.get(srcName) ?? getCurrentIrTypeScope()?.globals.get(srcName) ?? "auto";
|
|
864
|
+
// Element type of a vector/staticArray/cArray, else the type itself.
|
|
865
|
+
const elemType = (() => {
|
|
866
|
+
const e = elementOf(parseCppType(srcType));
|
|
867
|
+
return e ? renderCppType(e) : srcType;
|
|
868
|
+
})();
|
|
869
|
+
const zeroElements = [];
|
|
870
|
+
for (let zi = 0; zi < srcSize; zi++)
|
|
871
|
+
zeroElements.push({ kind: "number", value: 0 });
|
|
872
|
+
lowered.push({
|
|
873
|
+
kind: "var_decl", sourceSpan: span,
|
|
874
|
+
leadingComments: loweredDeclaration.leadingComments, trailingComments: [],
|
|
875
|
+
name: varName, storage: "let", cppType: "auto",
|
|
876
|
+
initializer: { kind: "array", elementType: elemType, elements: zeroElements },
|
|
877
|
+
});
|
|
878
|
+
lowered.push({
|
|
879
|
+
kind: "var_decl", sourceSpan: span,
|
|
880
|
+
leadingComments: [], trailingComments: [],
|
|
881
|
+
name: lenVar, storage: "let", cppType: "int",
|
|
882
|
+
initializer: { kind: "number", value: 0 },
|
|
883
|
+
});
|
|
884
|
+
filteredArrayLengthVars.set(varName, lenVar);
|
|
885
|
+
const conditionIR = expressionToIR(bodyExpr, sourceText, diagnostics);
|
|
886
|
+
lowered.push({
|
|
887
|
+
kind: "for",
|
|
888
|
+
sourceSpan: span,
|
|
889
|
+
initializer: { kind: "var_decl", sourceSpan: span, name: "__tc_i", storage: "let", cppType: "int", initializer: { kind: "number", value: 0 } },
|
|
890
|
+
condition: { kind: "binary", left: { kind: "identifier", value: "__tc_i" }, operator: "<", right: { kind: "number", value: srcSize } },
|
|
891
|
+
increment: { kind: "update", sourceSpan: span, target: "__tc_i", operator: "++", prefix: false },
|
|
892
|
+
body: [
|
|
893
|
+
{ kind: "var_decl", sourceSpan: span, name: paramName, storage: "const", cppType: "auto",
|
|
894
|
+
initializer: { kind: "raw", value: `${srcName}[__tc_i]` } },
|
|
895
|
+
{ kind: "if", sourceSpan: span,
|
|
896
|
+
condition: conditionIR,
|
|
897
|
+
thenBranch: [
|
|
898
|
+
{ kind: "assign", sourceSpan: span, target: `${varName}[${lenVar}]`, operator: "=",
|
|
899
|
+
value: { kind: "raw", value: `${srcName}[__tc_i]` } },
|
|
900
|
+
{ kind: "update", sourceSpan: span, target: lenVar, operator: "++", prefix: false },
|
|
901
|
+
],
|
|
902
|
+
},
|
|
903
|
+
],
|
|
904
|
+
});
|
|
905
|
+
commentsAssigned = true;
|
|
906
|
+
continue;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
if (ts.isCallExpression(actualInitializer) &&
|
|
911
|
+
ts.isPropertyAccessExpression(actualInitializer.expression) &&
|
|
912
|
+
actualInitializer.expression.name.text === "reduce" &&
|
|
913
|
+
ts.isIdentifier(actualInitializer.expression.expression)) {
|
|
914
|
+
const srcName = actualInitializer.expression.expression.text;
|
|
915
|
+
const srcSize = arrayLiteralSizes.get(srcName);
|
|
916
|
+
const arrowFn = actualInitializer.arguments[0];
|
|
917
|
+
const initVal = actualInitializer.arguments[1];
|
|
918
|
+
if (srcSize !== undefined && arrowFn && (ts.isArrowFunction(arrowFn) || ts.isFunctionExpression(arrowFn))) {
|
|
919
|
+
const accParam = arrowFn.parameters[0];
|
|
920
|
+
const valParam = arrowFn.parameters[1];
|
|
921
|
+
const accName = accParam && ts.isIdentifier(accParam.name) ? accParam.name.text : "__acc";
|
|
922
|
+
const valName = valParam && ts.isIdentifier(valParam.name) ? valParam.name.text : "__val";
|
|
923
|
+
const bodyExpr = ts.isBlock(arrowFn.body) ? undefined : arrowFn.body;
|
|
924
|
+
if (!initVal) {
|
|
925
|
+
diagnostics.push(makeDiagnostic(sourceText, actualInitializer.pos, "reduce() requires an initial value argument for C++ transpilation.", "warning", "TS2CPP_REDUCE_NO_INIT"));
|
|
926
|
+
}
|
|
927
|
+
if (bodyExpr && initVal) {
|
|
928
|
+
const span = loweredDeclaration.sourceSpan;
|
|
929
|
+
lowered.push({
|
|
930
|
+
kind: "var_decl", sourceSpan: span,
|
|
931
|
+
leadingComments: loweredDeclaration.leadingComments, trailingComments: [],
|
|
932
|
+
name: varName, storage: "let", cppType: "auto",
|
|
933
|
+
initializer: expressionToIR(initVal, sourceText, diagnostics),
|
|
934
|
+
});
|
|
935
|
+
const bodyIR = expressionToIR(bodyExpr, sourceText, diagnostics);
|
|
936
|
+
lowered.push({
|
|
937
|
+
kind: "for",
|
|
938
|
+
sourceSpan: span,
|
|
939
|
+
initializer: { kind: "var_decl", sourceSpan: span, name: "__tc_i", storage: "let", cppType: "int", initializer: { kind: "number", value: 0 } },
|
|
940
|
+
condition: { kind: "binary", left: { kind: "identifier", value: "__tc_i" }, operator: "<", right: { kind: "number", value: srcSize } },
|
|
941
|
+
increment: { kind: "update", sourceSpan: span, target: "__tc_i", operator: "++", prefix: false },
|
|
942
|
+
body: [
|
|
943
|
+
{ kind: "var_decl", sourceSpan: span, name: valName, storage: "const", cppType: "auto",
|
|
944
|
+
initializer: { kind: "raw", value: `${srcName}[__tc_i]` } },
|
|
945
|
+
{ kind: "var_decl", sourceSpan: span, name: accName, storage: "const", cppType: "auto",
|
|
946
|
+
initializer: { kind: "identifier", value: varName } },
|
|
947
|
+
{ kind: "assign", sourceSpan: span, target: varName, operator: "=", value: bodyIR },
|
|
948
|
+
],
|
|
949
|
+
});
|
|
950
|
+
commentsAssigned = true;
|
|
951
|
+
continue;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
if (ts.isCallExpression(actualInitializer) &&
|
|
956
|
+
ts.isPropertyAccessExpression(actualInitializer.expression) &&
|
|
957
|
+
actualInitializer.expression.name.text === "push" &&
|
|
958
|
+
ts.isIdentifier(actualInitializer.expression.expression)) {
|
|
959
|
+
const arrName = actualInitializer.expression.expression.text;
|
|
960
|
+
if (mutableArrayVars.has(arrName) && actualInitializer.arguments.length > 0) {
|
|
961
|
+
const span = loweredDeclaration.sourceSpan;
|
|
962
|
+
lowered.push({
|
|
963
|
+
kind: "call", sourceSpan: span,
|
|
964
|
+
callee: `${arrName}.push`,
|
|
965
|
+
args: [expressionToIR(actualInitializer.arguments[0], sourceText, diagnostics)],
|
|
966
|
+
});
|
|
967
|
+
lowered.push({
|
|
968
|
+
kind: "var_decl", sourceSpan: span,
|
|
969
|
+
leadingComments: loweredDeclaration.leadingComments, trailingComments: [],
|
|
970
|
+
name: varName, storage, cppType: "auto",
|
|
971
|
+
initializer: { kind: "raw", value: `${arrName}.size()` },
|
|
972
|
+
});
|
|
973
|
+
commentsAssigned = true;
|
|
974
|
+
continue;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
if (ts.isArrayLiteralExpression(actualInitializer) && !mutableArrayVars.has(varName)) {
|
|
978
|
+
const vecMatch = isVector(parseCppType(varCppType));
|
|
979
|
+
const inferredVecMatch = isVector(parseCppType(declarationType.inferredType));
|
|
980
|
+
if (!vecMatch && inferredVecMatch) {
|
|
981
|
+
activeArrayLiteralVars.add(varName);
|
|
982
|
+
loweredDeclaration.cppType = "auto";
|
|
983
|
+
localVariableTypes.set(varName, declarationType.inferredType);
|
|
984
|
+
setScopeLocalType(varName, declarationType.inferredType);
|
|
985
|
+
}
|
|
986
|
+
else if (!vecMatch) {
|
|
987
|
+
activeArrayLiteralVars.add(varName);
|
|
988
|
+
activeCArrayVars.add(varName);
|
|
989
|
+
loweredDeclaration.cppType = "auto";
|
|
990
|
+
localVariableTypes.set(varName, "auto");
|
|
991
|
+
setScopeLocalType(varName, "auto");
|
|
992
|
+
}
|
|
993
|
+
else if (vecMatch && !(getContext().activeStrategy?.needsStdVector() ?? true)) {
|
|
994
|
+
// Demo #33 Finding B — an explicitly array-TYPED non-mutated literal
|
|
995
|
+
// (`const SAMPLES: int32_t[] = [...]`) keeps its `std::vector<...>`
|
|
996
|
+
// cppType (so emit renders the right element type), but on a target
|
|
997
|
+
// that does NOT need std::vector (`!needsStdVector()`, e.g. Arduino
|
|
998
|
+
// AVR) it STILL emits as a RAW C array `T name[] = {...}` (the
|
|
999
|
+
// emit-side discriminator class-emitter.ts addCArrayIfNotMutable
|
|
1000
|
+
// uses exactly `!needsStdVector()`). Without tracking it in
|
|
1001
|
+
// activeArrayLiteralVars, `.length` resolution fell through to the
|
|
1002
|
+
// default `.size()` — invalid for a raw C array (avr-g++: "request
|
|
1003
|
+
// for member 'size' in 'SAMPLES', which is of non-class type").
|
|
1004
|
+
// The localVariableTypes entry keeps the `std::vector<...>` spelling
|
|
1005
|
+
// so resolveLengthProperty's container-vs-sizeof decision (which
|
|
1006
|
+
// checks needsStdVector + mutableArrayVars membership) routes
|
|
1007
|
+
// correctly.
|
|
1008
|
+
activeArrayLiteralVars.add(varName);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
const ownershipKind = extractOwnershipKindFromTypeNode(declaration.type, typeAliases);
|
|
1013
|
+
if (ownershipKind) {
|
|
1014
|
+
loweredDeclaration.ownershipKind = ownershipKind;
|
|
1015
|
+
}
|
|
1016
|
+
if (declarationType.shouldWarnUnmappedType) {
|
|
1017
|
+
diagnostics.push(makeDiagnostic(sourceText, declaration.pos, `Type annotation on '${declaration.name.text}' is not yet mapped; emitted as 'auto'.`, "warning", "TS2CPP_UNMAPPED_TYPE"));
|
|
1018
|
+
}
|
|
1019
|
+
if (ts.isIdentifier(declaration.name) && !actualInitializer && mutableArrayVars.has(declaration.name.text)) {
|
|
1020
|
+
loweredDeclaration.storage = "let";
|
|
1021
|
+
}
|
|
1022
|
+
lowered.push(loweredDeclaration);
|
|
1023
|
+
}
|
|
1024
|
+
return lowered;
|
|
1025
|
+
}
|
|
1026
|
+
export function collectPointerVars(statements) {
|
|
1027
|
+
const pointerVars = new Map();
|
|
1028
|
+
for (const statement of statements) {
|
|
1029
|
+
if (ts.isVariableStatement(statement)) {
|
|
1030
|
+
for (const decl of statement.declarationList.declarations) {
|
|
1031
|
+
if (ts.isIdentifier(decl.name) && decl.initializer && ts.isNewExpression(decl.initializer)) {
|
|
1032
|
+
const ctorText = decl.initializer.expression && ts.isIdentifier(decl.initializer.expression)
|
|
1033
|
+
? decl.initializer.expression.text : "";
|
|
1034
|
+
if (TYPED_ARRAY_ELEMENT_MAP[ctorText]) {
|
|
1035
|
+
activeCArrayVars.add(decl.name.text);
|
|
1036
|
+
}
|
|
1037
|
+
else {
|
|
1038
|
+
pointerVars.set(decl.name.text, ctorText);
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
return pointerVars;
|
|
1045
|
+
}
|