@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.
Files changed (377) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +79 -0
  3. package/dist/api/board-types.d.ts +1 -0
  4. package/dist/api/board-types.js +4 -0
  5. package/dist/api/capabilities.d.ts +12 -0
  6. package/dist/api/capabilities.js +1 -0
  7. package/dist/api/config.d.ts +120 -0
  8. package/dist/api/config.js +7 -0
  9. package/dist/api/index.d.ts +5 -0
  10. package/dist/api/index.js +2 -0
  11. package/dist/api/schema/builder.d.ts +14 -0
  12. package/dist/api/schema/builder.js +15 -0
  13. package/dist/api/schema/index.d.ts +2 -0
  14. package/dist/api/schema/index.js +1 -0
  15. package/dist/api/schema/types.d.ts +257 -0
  16. package/dist/api/schema/types.js +4 -0
  17. package/dist/api/shared/async-runtime-static.d.ts +8 -0
  18. package/dist/api/shared/async-runtime-static.js +201 -0
  19. package/dist/api/shared/async-types.d.ts +42 -0
  20. package/dist/api/shared/async-types.js +8 -0
  21. package/dist/api/shared/board-resolver.d.ts +12 -0
  22. package/dist/api/shared/board-resolver.js +7 -0
  23. package/dist/api/shared/cpp-type-ir.d.ts +229 -0
  24. package/dist/api/shared/cpp-type-ir.js +672 -0
  25. package/dist/api/shared/display-adapter.d.ts +12 -0
  26. package/dist/api/shared/display-adapter.js +145 -0
  27. package/dist/api/shared/display-adapters/eink-mono.d.ts +2 -0
  28. package/dist/api/shared/display-adapters/eink-mono.js +53 -0
  29. package/dist/api/shared/display-adapters/sdl.d.ts +2 -0
  30. package/dist/api/shared/display-adapters/sdl.js +452 -0
  31. package/dist/api/shared/display-adapters/ssd1309.d.ts +2 -0
  32. package/dist/api/shared/display-adapters/ssd1309.js +136 -0
  33. package/dist/api/shared/display-adapters/st7796.d.ts +2 -0
  34. package/dist/api/shared/display-adapters/st7796.js +132 -0
  35. package/dist/api/shared/display-capabilities.d.ts +45 -0
  36. package/dist/api/shared/display-capabilities.js +54 -0
  37. package/dist/api/shared/display-op-ir.d.ts +62 -0
  38. package/dist/api/shared/display-op-ir.js +19 -0
  39. package/dist/api/shared/display-profile.d.ts +265 -0
  40. package/dist/api/shared/display-profile.js +338 -0
  41. package/dist/api/shared/graphics-strategy.d.ts +29 -0
  42. package/dist/api/shared/graphics-strategy.js +11 -0
  43. package/dist/api/shared/hal-op-ir.d.ts +393 -0
  44. package/dist/api/shared/hal-op-ir.js +18 -0
  45. package/dist/api/shared/index.d.ts +26 -0
  46. package/dist/api/shared/index.js +21 -0
  47. package/dist/api/shared/ir-core.d.ts +363 -0
  48. package/dist/api/shared/ir-core.js +8 -0
  49. package/dist/api/shared/ir-declarations.d.ts +201 -0
  50. package/dist/api/shared/ir-declarations.js +26 -0
  51. package/dist/api/shared/ir.d.ts +85 -0
  52. package/dist/api/shared/ir.js +13 -0
  53. package/dist/api/shared/platform-strategy.d.ts +402 -0
  54. package/dist/api/shared/platform-strategy.js +14 -0
  55. package/dist/api/shared/polyfill-helper-registry.d.ts +11 -0
  56. package/dist/api/shared/polyfill-helper-registry.js +86 -0
  57. package/dist/api/shared/polyfill-types.d.ts +33 -0
  58. package/dist/api/shared/polyfill-types.js +76 -0
  59. package/dist/api/shared/promise-runtime.d.ts +10 -0
  60. package/dist/api/shared/promise-runtime.js +244 -0
  61. package/dist/api/shared/snprintf-types.d.ts +29 -0
  62. package/dist/api/shared/snprintf-types.js +8 -0
  63. package/dist/api/shared/string-method-registry.d.ts +53 -0
  64. package/dist/api/shared/string-method-registry.js +168 -0
  65. package/dist/api/shared/toolchain-types.d.ts +63 -0
  66. package/dist/api/shared/toolchain-types.js +71 -0
  67. package/dist/api/shared/types.d.ts +50 -0
  68. package/dist/api/shared/types.js +6 -0
  69. package/dist/ast/parse.d.ts +2 -0
  70. package/dist/ast/parse.js +4 -0
  71. package/dist/cache.d.ts +26 -0
  72. package/dist/cache.js +95 -0
  73. package/dist/cli-utils.d.ts +37 -0
  74. package/dist/cli-utils.js +250 -0
  75. package/dist/cli.d.ts +2 -0
  76. package/dist/cli.js +635 -0
  77. package/dist/config-loader.d.ts +78 -0
  78. package/dist/config-loader.js +508 -0
  79. package/dist/config-schema.d.ts +233 -0
  80. package/dist/config-schema.js +83 -0
  81. package/dist/create/board-checklist.d.ts +2 -0
  82. package/dist/create/board-checklist.js +52 -0
  83. package/dist/create/board-codegen.d.ts +9 -0
  84. package/dist/create/board-codegen.js +238 -0
  85. package/dist/create/board-generators.d.ts +12 -0
  86. package/dist/create/board-generators.js +652 -0
  87. package/dist/create/board-spec.d.ts +640 -0
  88. package/dist/create/board-spec.js +215 -0
  89. package/dist/create/eslint-rules-template.d.ts +2 -0
  90. package/dist/create/eslint-rules-template.js +672 -0
  91. package/dist/create/index.d.ts +11 -0
  92. package/dist/create/index.js +8 -0
  93. package/dist/create/init-scaffold.d.ts +27 -0
  94. package/dist/create/init-scaffold.js +150 -0
  95. package/dist/create/init-templates.d.ts +24 -0
  96. package/dist/create/init-templates.js +379 -0
  97. package/dist/create/init-wizard.d.ts +8 -0
  98. package/dist/create/init-wizard.js +168 -0
  99. package/dist/debug/breakpoint-loader.d.ts +24 -0
  100. package/dist/debug/breakpoint-loader.js +137 -0
  101. package/dist/debug/index.d.ts +3 -0
  102. package/dist/debug/index.js +7 -0
  103. package/dist/debug/preprocessor.d.ts +9 -0
  104. package/dist/debug/preprocessor.js +214 -0
  105. package/dist/debug/types.d.ts +32 -0
  106. package/dist/debug/types.js +6 -0
  107. package/dist/diagnostics/diagnostics-report.d.ts +51 -0
  108. package/dist/diagnostics/diagnostics-report.js +333 -0
  109. package/dist/diagnostics/json-schema.d.ts +138 -0
  110. package/dist/diagnostics/json-schema.js +27 -0
  111. package/dist/diagnostics/md-writer.d.ts +10 -0
  112. package/dist/diagnostics/md-writer.js +336 -0
  113. package/dist/diagnostics/mermaid-builder.d.ts +65 -0
  114. package/dist/diagnostics/mermaid-builder.js +681 -0
  115. package/dist/emit/cpp-emitter.d.ts +6 -0
  116. package/dist/emit/cpp-emitter.js +48 -0
  117. package/dist/emit/emitters/class-emitter.d.ts +2 -0
  118. package/dist/emit/emitters/class-emitter.js +523 -0
  119. package/dist/emit/emitters/emitter-context.d.ts +174 -0
  120. package/dist/emit/emitters/emitter-context.js +1 -0
  121. package/dist/emit/emitters/entrypoint-synthesizer.d.ts +2 -0
  122. package/dist/emit/emitters/entrypoint-synthesizer.js +175 -0
  123. package/dist/emit/emitters/function-emitter-impl.d.ts +19 -0
  124. package/dist/emit/emitters/function-emitter-impl.js +428 -0
  125. package/dist/emit/emitters/line-appender.d.ts +15 -0
  126. package/dist/emit/emitters/line-appender.js +344 -0
  127. package/dist/emit/emitters/namespace-emitter.d.ts +2 -0
  128. package/dist/emit/emitters/namespace-emitter.js +324 -0
  129. package/dist/emit/emitters/output-finalizer.d.ts +4 -0
  130. package/dist/emit/emitters/output-finalizer.js +267 -0
  131. package/dist/emit/emitters/setup.d.ts +3 -0
  132. package/dist/emit/emitters/setup.js +851 -0
  133. package/dist/emit/emitters/top-level-prep.d.ts +2 -0
  134. package/dist/emit/emitters/top-level-prep.js +706 -0
  135. package/dist/emit/emitters/type-decl-emitter.d.ts +2 -0
  136. package/dist/emit/emitters/type-decl-emitter.js +251 -0
  137. package/dist/emit/emitters/ui-emitter.d.ts +21 -0
  138. package/dist/emit/emitters/ui-emitter.js +564 -0
  139. package/dist/emit/enum-emitter.d.ts +1 -0
  140. package/dist/emit/enum-emitter.js +1 -0
  141. package/dist/emit/expression-renderer.d.ts +283 -0
  142. package/dist/emit/expression-renderer.js +1477 -0
  143. package/dist/emit/native-helpers-emitter.d.ts +11 -0
  144. package/dist/emit/native-helpers-emitter.js +34 -0
  145. package/dist/emit/route-hal-op.d.ts +5 -0
  146. package/dist/emit/route-hal-op.js +13 -0
  147. package/dist/emit/snprintf-helpers.d.ts +17 -0
  148. package/dist/emit/snprintf-helpers.js +462 -0
  149. package/dist/emit/statement-renderer.d.ts +189 -0
  150. package/dist/emit/statement-renderer.js +948 -0
  151. package/dist/emit/utils/async-state-machine.d.ts +42 -0
  152. package/dist/emit/utils/async-state-machine.js +343 -0
  153. package/dist/emit/utils/comment-helpers.d.ts +20 -0
  154. package/dist/emit/utils/comment-helpers.js +31 -0
  155. package/dist/emit/utils/cpp-helpers.d.ts +47 -0
  156. package/dist/emit/utils/cpp-helpers.js +85 -0
  157. package/dist/emit/utils/include-resolver.d.ts +58 -0
  158. package/dist/emit/utils/include-resolver.js +155 -0
  159. package/dist/emit/utils/index.d.ts +8 -0
  160. package/dist/emit/utils/index.js +12 -0
  161. package/dist/emit/utils/type-inference.d.ts +159 -0
  162. package/dist/emit/utils/type-inference.js +559 -0
  163. package/dist/eslint-check.d.ts +10 -0
  164. package/dist/eslint-check.js +102 -0
  165. package/dist/framework-package.d.ts +10 -0
  166. package/dist/framework-package.js +58 -0
  167. package/dist/framework-registry.d.ts +39 -0
  168. package/dist/framework-registry.js +30 -0
  169. package/dist/incremental-cache.d.ts +116 -0
  170. package/dist/incremental-cache.js +335 -0
  171. package/dist/ir/adc-range-validation.d.ts +11 -0
  172. package/dist/ir/adc-range-validation.js +250 -0
  173. package/dist/ir/ast-node-utils.d.ts +8 -0
  174. package/dist/ir/ast-node-utils.js +32 -0
  175. package/dist/ir/board-pin-utils.d.ts +13 -0
  176. package/dist/ir/board-pin-utils.js +65 -0
  177. package/dist/ir/board-resolver.d.ts +36 -0
  178. package/dist/ir/board-resolver.js +437 -0
  179. package/dist/ir/build-ir-state.d.ts +167 -0
  180. package/dist/ir/build-ir-state.js +306 -0
  181. package/dist/ir/build-ir.d.ts +2 -0
  182. package/dist/ir/build-ir.js +808 -0
  183. package/dist/ir/call-graph.d.ts +35 -0
  184. package/dist/ir/call-graph.js +194 -0
  185. package/dist/ir/compile-time-only.d.ts +2 -0
  186. package/dist/ir/compile-time-only.js +16 -0
  187. package/dist/ir/declaration-builders.d.ts +9 -0
  188. package/dist/ir/declaration-builders.js +608 -0
  189. package/dist/ir/entry-points.d.ts +30 -0
  190. package/dist/ir/entry-points.js +191 -0
  191. package/dist/ir/expression-to-ir.d.ts +5 -0
  192. package/dist/ir/expression-to-ir.js +2249 -0
  193. package/dist/ir/extract-property-chain.d.ts +10 -0
  194. package/dist/ir/extract-property-chain.js +30 -0
  195. package/dist/ir/feature-prescan.d.ts +3 -0
  196. package/dist/ir/feature-prescan.js +43 -0
  197. package/dist/ir/feature-registry.d.ts +30 -0
  198. package/dist/ir/feature-registry.js +816 -0
  199. package/dist/ir/filter.d.ts +7 -0
  200. package/dist/ir/filter.js +85 -0
  201. package/dist/ir/function-builder.d.ts +19 -0
  202. package/dist/ir/function-builder.js +670 -0
  203. package/dist/ir/hal/hal-emitter.d.ts +62 -0
  204. package/dist/ir/hal/hal-emitter.js +735 -0
  205. package/dist/ir/hal/hal-parser.d.ts +61 -0
  206. package/dist/ir/hal/hal-parser.js +473 -0
  207. package/dist/ir/hal/hal-plugins.d.ts +34 -0
  208. package/dist/ir/hal/hal-plugins.js +706 -0
  209. package/dist/ir/hal-resolver.d.ts +5 -0
  210. package/dist/ir/hal-resolver.js +5 -0
  211. package/dist/ir/heap-analysis.d.ts +8 -0
  212. package/dist/ir/heap-analysis.js +350 -0
  213. package/dist/ir/heap-array-validation.d.ts +24 -0
  214. package/dist/ir/heap-array-validation.js +29 -0
  215. package/dist/ir/identifier-collector.d.ts +18 -0
  216. package/dist/ir/identifier-collector.js +439 -0
  217. package/dist/ir/interrupt-analysis.d.ts +39 -0
  218. package/dist/ir/interrupt-analysis.js +467 -0
  219. package/dist/ir/memory-budget-validation.d.ts +10 -0
  220. package/dist/ir/memory-budget-validation.js +86 -0
  221. package/dist/ir/namespace-builder.d.ts +7 -0
  222. package/dist/ir/namespace-builder.js +152 -0
  223. package/dist/ir/ownership-analysis.d.ts +9 -0
  224. package/dist/ir/ownership-analysis.js +1207 -0
  225. package/dist/ir/peripheral-ownership.d.ts +3 -0
  226. package/dist/ir/peripheral-ownership.js +152 -0
  227. package/dist/ir/peripheral-symbols.d.ts +7 -0
  228. package/dist/ir/peripheral-symbols.js +57 -0
  229. package/dist/ir/peripheral-usage.d.ts +54 -0
  230. package/dist/ir/peripheral-usage.js +691 -0
  231. package/dist/ir/peripheral-validation.d.ts +8 -0
  232. package/dist/ir/peripheral-validation.js +77 -0
  233. package/dist/ir/pin-alias-conflict.d.ts +4 -0
  234. package/dist/ir/pin-alias-conflict.js +34 -0
  235. package/dist/ir/pin-capability-validation.d.ts +6 -0
  236. package/dist/ir/pin-capability-validation.js +413 -0
  237. package/dist/ir/pin-mode-validation.d.ts +10 -0
  238. package/dist/ir/pin-mode-validation.js +301 -0
  239. package/dist/ir/pin-safety.d.ts +11 -0
  240. package/dist/ir/pin-safety.js +58 -0
  241. package/dist/ir/program-analysis.d.ts +44 -0
  242. package/dist/ir/program-analysis.js +589 -0
  243. package/dist/ir/pulldown-validation.d.ts +7 -0
  244. package/dist/ir/pulldown-validation.js +49 -0
  245. package/dist/ir/pwm-timer-sharing.d.ts +4 -0
  246. package/dist/ir/pwm-timer-sharing.js +93 -0
  247. package/dist/ir/reachability.d.ts +68 -0
  248. package/dist/ir/reachability.js +224 -0
  249. package/dist/ir/register-decorators.d.ts +6 -0
  250. package/dist/ir/register-decorators.js +49 -0
  251. package/dist/ir/render-expr.d.ts +4 -0
  252. package/dist/ir/render-expr.js +127 -0
  253. package/dist/ir/resource-analysis.d.ts +19 -0
  254. package/dist/ir/resource-analysis.js +153 -0
  255. package/dist/ir/statement-to-ir.d.ts +23 -0
  256. package/dist/ir/statement-to-ir.js +349 -0
  257. package/dist/ir/symbol-table.d.ts +68 -0
  258. package/dist/ir/symbol-table.js +179 -0
  259. package/dist/ir/symbol-types.d.ts +56 -0
  260. package/dist/ir/symbol-types.js +107 -0
  261. package/dist/ir/timer0-pwm-timing-conflict.d.ts +4 -0
  262. package/dist/ir/timer0-pwm-timing-conflict.js +71 -0
  263. package/dist/ir/timing-validation.d.ts +7 -0
  264. package/dist/ir/timing-validation.js +95 -0
  265. package/dist/ir/transformers/array-methods.d.ts +8 -0
  266. package/dist/ir/transformers/array-methods.js +493 -0
  267. package/dist/ir/transformers/call-statement.d.ts +5 -0
  268. package/dist/ir/transformers/call-statement.js +758 -0
  269. package/dist/ir/transformers/callback-context-registry.d.ts +20 -0
  270. package/dist/ir/transformers/callback-context-registry.js +55 -0
  271. package/dist/ir/transformers/canvas-lowering.d.ts +42 -0
  272. package/dist/ir/transformers/canvas-lowering.js +249 -0
  273. package/dist/ir/transformers/control-flow.d.ts +8 -0
  274. package/dist/ir/transformers/control-flow.js +508 -0
  275. package/dist/ir/transformers/expressions.d.ts +7 -0
  276. package/dist/ir/transformers/expressions.js +406 -0
  277. package/dist/ir/transformers/hal-call-resolver.d.ts +29 -0
  278. package/dist/ir/transformers/hal-call-resolver.js +274 -0
  279. package/dist/ir/transformers/hal-emit-helpers.d.ts +14 -0
  280. package/dist/ir/transformers/hal-emit-helpers.js +63 -0
  281. package/dist/ir/transformers/namespace-methods.d.ts +20 -0
  282. package/dist/ir/transformers/namespace-methods.js +110 -0
  283. package/dist/ir/transformers/register-assignment.d.ts +5 -0
  284. package/dist/ir/transformers/register-assignment.js +56 -0
  285. package/dist/ir/transformers/ui-call-resolver.d.ts +149 -0
  286. package/dist/ir/transformers/ui-call-resolver.js +1136 -0
  287. package/dist/ir/transformers/ui-callback-lowering.d.ts +62 -0
  288. package/dist/ir/transformers/ui-callback-lowering.js +623 -0
  289. package/dist/ir/transformers/ui-mount.d.ts +20 -0
  290. package/dist/ir/transformers/ui-mount.js +35 -0
  291. package/dist/ir/transformers/ui-reactive.d.ts +68 -0
  292. package/dist/ir/transformers/ui-reactive.js +129 -0
  293. package/dist/ir/transformers/variables.d.ts +14 -0
  294. package/dist/ir/transformers/variables.js +1045 -0
  295. package/dist/ir/try-catch-validation.d.ts +26 -0
  296. package/dist/ir/try-catch-validation.js +128 -0
  297. package/dist/ir/type-resolution.d.ts +31 -0
  298. package/dist/ir/type-resolution.js +977 -0
  299. package/dist/ir/ui-element-auto-wire.d.ts +50 -0
  300. package/dist/ir/ui-element-auto-wire.js +218 -0
  301. package/dist/ir/unit-suspicion-validation.d.ts +12 -0
  302. package/dist/ir/unit-suspicion-validation.js +296 -0
  303. package/dist/ir/utils/walk-ir.d.ts +6 -0
  304. package/dist/ir/utils/walk-ir.js +162 -0
  305. package/dist/ir/validation-orchestrator.d.ts +4 -0
  306. package/dist/ir/validation-orchestrator.js +45 -0
  307. package/dist/libdef/base-class-resolver.d.ts +23 -0
  308. package/dist/libdef/base-class-resolver.js +50 -0
  309. package/dist/libdef/cpp-to-decl.d.ts +34 -0
  310. package/dist/libdef/cpp-to-decl.js +584 -0
  311. package/dist/libdef/header-parser.d.ts +53 -0
  312. package/dist/libdef/header-parser.js +269 -0
  313. package/dist/libdef/registry.d.ts +10 -0
  314. package/dist/libdef/registry.js +142 -0
  315. package/dist/mapping/peripheral-names.d.ts +23 -0
  316. package/dist/mapping/peripheral-names.js +63 -0
  317. package/dist/mapping/source-map.d.ts +46 -0
  318. package/dist/mapping/source-map.js +207 -0
  319. package/dist/orchestrator/dts-generator.d.ts +17 -0
  320. package/dist/orchestrator/dts-generator.js +83 -0
  321. package/dist/orchestrator/graph-builder.d.ts +18 -0
  322. package/dist/orchestrator/graph-builder.js +218 -0
  323. package/dist/orchestrator/semantic-facts-verifier.d.ts +38 -0
  324. package/dist/orchestrator/semantic-facts-verifier.js +121 -0
  325. package/dist/orchestrator/semantic-facts.d.ts +141 -0
  326. package/dist/orchestrator/semantic-facts.js +652 -0
  327. package/dist/orchestrator/type-checker.d.ts +39 -0
  328. package/dist/orchestrator/type-checker.js +760 -0
  329. package/dist/platform/async-runtime.d.ts +8 -0
  330. package/dist/platform/async-runtime.js +26 -0
  331. package/dist/platform/generic-strategy.d.ts +101 -0
  332. package/dist/platform/generic-strategy.js +289 -0
  333. package/dist/platform/index.d.ts +4 -0
  334. package/dist/platform/index.js +9 -0
  335. package/dist/platform/registry.d.ts +21 -0
  336. package/dist/platform/registry.js +42 -0
  337. package/dist/platform/toolchain.d.ts +4 -0
  338. package/dist/platform/toolchain.js +43 -0
  339. package/dist/preview/client.d.ts +1 -0
  340. package/dist/preview/client.js +137 -0
  341. package/dist/preview/server.d.ts +5 -0
  342. package/dist/preview/server.js +199 -0
  343. package/dist/profiler/index.d.ts +28 -0
  344. package/dist/profiler/index.js +28 -0
  345. package/dist/profiler/profiler.d.ts +95 -0
  346. package/dist/profiler/profiler.js +338 -0
  347. package/dist/profiler/types.d.ts +87 -0
  348. package/dist/profiler/types.js +12 -0
  349. package/dist/stores/display-profile-store.d.ts +16 -0
  350. package/dist/stores/display-profile-store.js +48 -0
  351. package/dist/stores/theme-store.d.ts +13 -0
  352. package/dist/stores/theme-store.js +30 -0
  353. package/dist/testing.d.ts +45 -0
  354. package/dist/testing.js +47 -0
  355. package/dist/transpile/resolution.d.ts +29 -0
  356. package/dist/transpile/resolution.js +282 -0
  357. package/dist/transpile.d.ts +3 -0
  358. package/dist/transpile.js +722 -0
  359. package/dist/types.d.ts +217 -0
  360. package/dist/types.js +1 -0
  361. package/dist/ui/ui-bridge.d.ts +4 -0
  362. package/dist/ui/ui-bridge.js +34 -0
  363. package/dist/ui-hook.d.ts +84 -0
  364. package/dist/ui-hook.js +45 -0
  365. package/dist/utils/cli.d.ts +3 -0
  366. package/dist/utils/cli.js +463 -0
  367. package/dist/utils/fs.d.ts +4 -0
  368. package/dist/utils/fs.js +23 -0
  369. package/dist/utils/logger.d.ts +2 -0
  370. package/dist/utils/logger.js +8 -0
  371. package/dist/utils/strings.d.ts +35 -0
  372. package/dist/utils/strings.js +142 -0
  373. package/dist/utils/ui.d.ts +66 -0
  374. package/dist/utils/ui.js +149 -0
  375. package/dist/watch.d.ts +35 -0
  376. package/dist/watch.js +111 -0
  377. 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
+ }