@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,948 @@
1
+ /**
2
+ * Statement Renderer for C++ code emission.
3
+ * Encapsulates all statement rendering logic with explicit dependencies.
4
+ * Extracted from cpp-emitter.ts
5
+ */
6
+ import { routeHALOp } from "./route-hal-op.js";
7
+ import { activeNamespaceNames } from "../ir/build-ir-state.js";
8
+ import { ExpressionRenderer, transformTypeName, normalizeRawExpression } from "./expression-renderer.js";
9
+ import { isConsoleCall, getConsoleMethod, inferObjectFieldType, collectNestedStructDefs } from "./utils/index.js";
10
+ import { escapeCppKeyword, escapeTrailingMember } from "../utils/strings.js";
11
+ import { accessorGetterName, accessorSetterName } from "./utils/cpp-helpers.js";
12
+ import { parseCppType, bareType, parsedIsPointer, parsedIsVector, parsedElementString, parsedIsPlainStructType } from "../api/shared/cpp-type-ir.js";
13
+ /**
14
+ * Returns true for C++ scalar/primitive types that are cheaply passed by value.
15
+ * Non-primitives (std::vector, String, structs, arrays) should be passed by reference
16
+ * when borrowed via Shared<T> or Mutable<T> to avoid deep copies.
17
+ */
18
+ function isPrimitiveCppType(cppType) {
19
+ const t = cppType.trim();
20
+ const primitives = new Set([
21
+ 'int', 'float', 'double', 'bool', 'char', 'long', 'void',
22
+ 'uint8_t', 'uint16_t', 'uint32_t', 'uint64_t',
23
+ 'int8_t', 'int16_t', 'int32_t', 'int64_t',
24
+ 'size_t', 'byte', 'word',
25
+ 'unsigned int', 'unsigned long', 'unsigned char',
26
+ 'signed int', 'signed long', 'signed char',
27
+ ]);
28
+ return primitives.has(t);
29
+ }
30
+ function isIndirectType(cppType, strategy) {
31
+ // Pointer or C-array. Detected structurally rather than by regex.
32
+ if (strategy.isPointerType(cppType))
33
+ return true;
34
+ const ir = parseCppType(cppType);
35
+ return ir.kind === "cArray" || ir.kind === "staticArray";
36
+ }
37
+ /** True for any `std::`-prefixed type (vector/map/set/tuple/variant/function/string).
38
+ * Replaces the historical `cppType.startsWith('std::')` check. */
39
+ function parsedIsStdContainer(cppType) {
40
+ const ir = parseCppType(cppType);
41
+ switch (ir.kind) {
42
+ case "vector":
43
+ case "map":
44
+ case "set":
45
+ case "tuple":
46
+ case "variant":
47
+ case "function":
48
+ case "string":
49
+ case "smartPointer":
50
+ return true;
51
+ default:
52
+ return false;
53
+ }
54
+ }
55
+ /**
56
+ * True if a C++ return type denotes a STRUCT (value class/interface) rather
57
+ * than a primitive, pointer, void, container, or enum. Used to decide whether
58
+ * `return null` must lower to `return {};` (value-init) instead of a nullish
59
+ * literal. Demo #14 Finding A.
60
+ */
61
+ function isStructReturnType(cppType, strategy) {
62
+ const t = cppType.trim();
63
+ if (!t || t === "void" || t === "auto")
64
+ return false;
65
+ if (isPrimitiveCppType(t))
66
+ return false;
67
+ if (isIndirectType(t, strategy))
68
+ return false;
69
+ // Containers and pseudo-types (std::vector, std::string, __tc_StaticArray,
70
+ // __tc_str_ptr, std::function, …) already value-initialize from {} or their
71
+ // own defaults. Restrict this path to plain struct names. Detected by "the
72
+ // parsed IR is a bare named type (no std::/__tc_ wrapper)".
73
+ const ir = parseCppType(t);
74
+ const bare = bareType(ir);
75
+ if (bare.kind !== "named")
76
+ return false;
77
+ // Exclude the pseudo-type markers themselves (which parse to their own kinds,
78
+ // not `named`, so this is belt-and-braces).
79
+ return !t.startsWith("__tc_");
80
+ }
81
+ /**
82
+ * True if a return-value expression lowers to a nullish literal — `null`
83
+ * (`nullptr`), `undefined` (`CUTTLEFISH_UNDEFINED`), or a nullish-coalescing
84
+ * `cuttlefish_nullish(x, nullptr)`/`typecad_nullish(...)` whose fallback is
85
+ * nullish. Such a value cannot convert to a struct return type. Demo #14 A.
86
+ */
87
+ function isNullishReturnValue(value) {
88
+ if (value.kind === "identifier") {
89
+ // `null` → "nullptr" (escaped to nullptr_ at render), `undefined` → the
90
+ // literal identifier "undefined" or the CUTTLEFISH_UNDEFINED macro.
91
+ return value.value === "nullptr" || value.value === "nullptr_" || value.value === "undefined" || value.value === "CUTTLEFISH_UNDEFINED";
92
+ }
93
+ // `x ?? null` lowers to a raw `cuttlefish_nullish(x, nullptr)` /
94
+ // `typecad_nullish(x, nullptr)`. The fallback is the struct-incompatible
95
+ // part; treat the whole expression as nullish for return purposes.
96
+ if (value.kind === "raw") {
97
+ const v = value.value;
98
+ return /(?:cuttlefish|typecad)_nullish\([^,]*,\s*(?:nullptr|CUTTLEFISH_UNDEFINED)\s*\)/.test(v);
99
+ }
100
+ return false;
101
+ }
102
+ /**
103
+ * Renders StatementIR nodes to C++ code strings.
104
+ */
105
+ export class StatementRenderer {
106
+ constructor(context) {
107
+ this.strategy = context.strategy;
108
+ this.knownFunctionReturnTypes = context.knownFunctionReturnTypes;
109
+ this.pointerVarTypes = context.pointerVarTypes;
110
+ this.globalPointerVarTypes = context.globalPointerVarTypes;
111
+ this.classNameMap = context.classNameMap;
112
+ this.varAccessorNames = context.varAccessorNames ?? new Map();
113
+ this.crossModuleClassNames = context.crossModuleClassNames;
114
+ this.enumNames = context.enumNames;
115
+ this.stringEnumNames = context.stringEnumNames ?? new Set();
116
+ this.namespaceNames = context.namespaceNames ?? new Set();
117
+ this.interfaceFieldTypes = context.interfaceFieldTypes ?? new Map();
118
+ this._diagnostics = context.diagnostics ?? [];
119
+ // Create expression renderer with shared context
120
+ this.expressionRenderer = new ExpressionRenderer({
121
+ strategy: context.strategy,
122
+ boardConstants: context.boardConstants,
123
+ classNameMap: context.classNameMap,
124
+ enumNames: context.enumNames,
125
+ stringEnumNames: context.stringEnumNames,
126
+ largeEnumNames: context.largeEnumNames,
127
+ knownFunctionReturnTypes: context.knownFunctionReturnTypes,
128
+ knownVariableTypes: context.knownVariableTypes,
129
+ pointerVarTypes: context.pointerVarTypes,
130
+ stringVarNames: context.stringVarNames,
131
+ cArrayVarNames: context.cArrayVarNames,
132
+ namespaceNames: context.namespaceNames,
133
+ varAccessorNames: context.varAccessorNames,
134
+ typeAccessorNames: context.typeAccessorNames,
135
+ crossModuleClassNames: context.crossModuleClassNames,
136
+ snprintfCounter: context.snprintfCounter,
137
+ interfaceFieldTypes: this.interfaceFieldTypes,
138
+ diagnostics: this._diagnostics,
139
+ });
140
+ }
141
+ /**
142
+ * Gets the underlying expression renderer for direct use.
143
+ */
144
+ getExprRenderer() {
145
+ return this.expressionRenderer;
146
+ }
147
+ /**
148
+ * Renders a statement IR node to a C++ string, including any snprintf
149
+ * prelude lines accumulated during expression rendering.
150
+ *
151
+ * @param statement The statement to render
152
+ * @param forHeader Whether this is for a header file (no semicolons)
153
+ * @param calleeTransformer Optional transformer for callee names
154
+ * @param knownVariableTypes Optional override for variable type mapping
155
+ * @returns Object with prelude lines and the rendered statement
156
+ */
157
+ renderWithPrelude(statement, forHeader = false, calleeTransformer, knownVariableTypes) {
158
+ this.expressionRenderer.clearPrelude();
159
+ const rendered = this.render(statement, forHeader, calleeTransformer, knownVariableTypes);
160
+ let prelude = this.expressionRenderer.drainPrelude();
161
+ // When returning a snprintf buffer, make it static so the pointer remains
162
+ // valid after the function returns (avoids dangling pointer to local stack).
163
+ if (statement.kind === "return" && prelude.length > 0) {
164
+ prelude = prelude.map(line => line.replace(/^char /, "static char "));
165
+ }
166
+ return { prelude, statement: rendered };
167
+ }
168
+ /**
169
+ * Renders a statement IR node to a C++ string.
170
+ *
171
+ * @param statement The statement to render
172
+ * @param forHeader Whether this is for a header file (no semicolons)
173
+ * @param calleeTransformer Optional transformer for callee names
174
+ * @param knownVariableTypes Optional override for variable type mapping
175
+ * @returns The C++ code string
176
+ */
177
+ render(statement, forHeader = false, calleeTransformer, knownVariableTypes) {
178
+ const rendered = (() => {
179
+ if (statement.kind === "call") {
180
+ return this.renderCall(statement, forHeader, calleeTransformer, knownVariableTypes);
181
+ }
182
+ if (statement.kind === "assign") {
183
+ // `statement.target` may be a compound member-access string
184
+ // (`this.field`, `obj->field`, `obj.field`) — NOT a bare identifier.
185
+ // escapeCppKeyword renames only WHOLE identifiers that are in the
186
+ // reserved set, so applying it to `this.min` leaves `min` untouched
187
+ // even when `min` is a reserved Arduino macro. That diverged from
188
+ // renderPropertyAccess (which escapes just the trailing property
189
+ // name), so a field declared `min_` (via renameStructField) was
190
+ // written as `this->min = ...` on assignment but read as
191
+ // `this->min_` on access — a declaration/access rename mismatch that
192
+ // fails at g++ time. Escape the trailing member name uniformly here.
193
+ let target = escapeTrailingMember(statement.target, this.strategy.reservedNames());
194
+ target = this.arrowGlobalPointerTarget(target);
195
+ // A namespace member used as an ASSIGNMENT TARGET must use `::`
196
+ // (scope resolution), not `.` — a namespace is not an object. The
197
+ // property-READ path already does this via namespaceNames; the
198
+ // assign-target path did not, so `Devices.total = ...` emitted with
199
+ // `.` and failed at g++ time (namespace stress test Finding 3).
200
+ const nsTargetMatch = target.match(/^([A-Za-z_$][\w$]*)\.([A-Za-z_$][\w$]*)/);
201
+ if (nsTargetMatch && this.namespaceNames.has(nsTargetMatch[1])) {
202
+ target = target.replace(/^([A-Za-z_$][\w$]*)\./, "$1::");
203
+ }
204
+ // Multi-level static access target: `Ns::Class.member` (a namespace,
205
+ // then a static class member). After the single-level rewrite above,
206
+ // a `Devices.Registry.count = ...` target is `Devices::Registry.count`.
207
+ // `Registry.count` is a static class member, so the second access is
208
+ // also `::` → `Devices::Registry::count` (namespace stress test
209
+ // Finding 4). Only fires when the target begins with a `::`-qualified
210
+ // namespace path (so a genuine `obj.field` instance access is
211
+ // untouched).
212
+ const nsClassTargetMatch = target.match(/^([A-Za-z_$][\w$]*)::([A-Za-z_$][\w$]*)\.([A-Za-z_$][\w$]*)/);
213
+ if (nsClassTargetMatch && this.namespaceNames.has(nsClassTargetMatch[1])) {
214
+ target = target.replace(/^([A-Za-z_$][\w$]*)::([A-Za-z_$][\w$]*)\./, "$1::$2::");
215
+ }
216
+ // Rewrite setter assignments: c->count = val → c->setCount(val)
217
+ if (statement.operator === "=" || statement.operator === "+=" || statement.operator === "-=") {
218
+ const setterMatch = target.match(/^(.+?)(->|\.)(\w+)$/);
219
+ if (setterMatch) {
220
+ const [, objStr, sep, propName] = setterMatch;
221
+ const varName = objStr.trim();
222
+ const accessors = this.varAccessorNames.get(varName);
223
+ if (accessors?.has(propName)) {
224
+ const kind = accessors.get(propName);
225
+ if (kind === "setter" || kind === "both") {
226
+ const setterName = accessorSetterName(propName);
227
+ const renderedValue = this.expressionRenderer.render(statement.value, undefined, knownVariableTypes);
228
+ if (statement.operator === "=") {
229
+ return forHeader
230
+ ? `${varName}${sep}${setterName}(${renderedValue})`
231
+ : `${varName}${sep}${setterName}(${renderedValue});`;
232
+ }
233
+ const getterName = accessorGetterName(propName);
234
+ const op = statement.operator.replace("=", "");
235
+ return forHeader
236
+ ? `${varName}${sep}${setterName}(${varName}${sep}${getterName}() ${op} ${renderedValue})`
237
+ : `${varName}${sep}${setterName}(${varName}${sep}${getterName}() ${op} ${renderedValue});`;
238
+ }
239
+ }
240
+ }
241
+ }
242
+ // Render the assigned value type-aware: when the target's resolved
243
+ // C++ type sits on the other side of an enum↔integral boundary from
244
+ // the value (e.g. `this->nxt[r][c] = next` where `nxt` is
245
+ // `vector<vector<uint8_t>>` and `next` is an `enum class Cell`),
246
+ // `renderValueForTarget` inserts the required `static_cast`. Falls
247
+ // through to plain rendering when the target type is unknown. Demo #32 A.
248
+ const assignTargetType = this.expressionRenderer.inferLvalueCppType(statement.target, knownVariableTypes);
249
+ const renderedAssignValue = assignTargetType
250
+ ? this.expressionRenderer.renderValueForTarget(statement.value, assignTargetType, knownVariableTypes)
251
+ : this.expressionRenderer.render(statement.value, undefined, knownVariableTypes);
252
+ return forHeader
253
+ ? `${target} ${statement.operator} ${renderedAssignValue}`
254
+ : `${target} ${statement.operator} ${renderedAssignValue};`;
255
+ }
256
+ if (statement.kind === "update") {
257
+ let target = escapeCppKeyword(statement.target, this.strategy.reservedNames());
258
+ target = this.arrowGlobalPointerTarget(target);
259
+ return statement.prefix
260
+ ? `${statement.operator}${target}${forHeader ? "" : ";"}`
261
+ : `${target}${statement.operator}${forHeader ? "" : ";"}`;
262
+ }
263
+ if (statement.kind === "return") {
264
+ if (!statement.value)
265
+ return "return;";
266
+ // When the enclosing function returns a STRUCT (a non-primitive,
267
+ // non-pointer value type such as an interface), a `return null` /
268
+ // `return undefined` (or `return map.get(k) ?? null`) cannot lower to
269
+ // `return nullptr;` / `return CUTTLEFISH_UNDEFINED;` — those are
270
+ // integer/pointer literals that won't convert to the struct type.
271
+ // Lower to a value-initialized `return {};` instead. The `T | null`
272
+ // union already strips to `T` (SUPPORT_MATRIX §1.8), so the function
273
+ // signature really does return the struct. Demo #14 Finding A.
274
+ const retType = statement.functionReturnType;
275
+ if (retType && isStructReturnType(retType, this.strategy) && isNullishReturnValue(statement.value)) {
276
+ return `return {};`;
277
+ }
278
+ const renderedValue = retType && this.enumNames.has(retType)
279
+ ? this.expressionRenderer.render(statement.value, undefined, knownVariableTypes)
280
+ : this.expressionRenderer.renderEnumSafeValue(statement.value, knownVariableTypes);
281
+ return `return ${renderedValue};`;
282
+ }
283
+ if (statement.kind === "while") {
284
+ return `while (${this.expressionRenderer.render(statement.condition, undefined, knownVariableTypes)})`;
285
+ }
286
+ if (statement.kind === "if") {
287
+ return `if (${this.expressionRenderer.render(statement.condition, undefined, knownVariableTypes)})`;
288
+ }
289
+ if (statement.kind === "for") {
290
+ let init = "";
291
+ if (statement.initializer) {
292
+ if (statement.initializer.kind === "var_decl" && statement.initializer.storage === "var") {
293
+ const declaredType = this.normalizeCppType(statement.initializer.cppType);
294
+ this.expressionRenderer.pushPrelude([`${declaredType} ${escapeCppKeyword(statement.initializer.name, this.strategy.reservedNames())};`]);
295
+ const safeName = escapeCppKeyword(statement.initializer.name, this.strategy.reservedNames());
296
+ init = statement.initializer.initializer
297
+ ? `${safeName} = ${this.expressionRenderer.render(statement.initializer.initializer, calleeTransformer, knownVariableTypes)}`
298
+ : safeName;
299
+ }
300
+ else {
301
+ init = this.render(statement.initializer, true, calleeTransformer, knownVariableTypes);
302
+ }
303
+ }
304
+ const cond = statement.condition ? this.expressionRenderer.render(statement.condition, undefined, knownVariableTypes) : "";
305
+ const incr = statement.increment ? this.render(statement.increment, true, calleeTransformer, knownVariableTypes) : "";
306
+ return `for (${init}; ${cond}; ${incr})`;
307
+ }
308
+ if (statement.kind === "for_of") {
309
+ const varDecl = statement.variable;
310
+ if (varDecl.kind === "var_decl") {
311
+ // Range-for by reference for non-primitive element types (structs,
312
+ // classes, strings) to avoid the per-iteration copy g++ warns about
313
+ // (-Wrange-loop-construct). Primitives stay by value.
314
+ const isRef = !isPrimitiveCppType(varDecl.cppType) && !isIndirectType(varDecl.cppType, this.strategy);
315
+ return `for (${this.renderTypedName(varDecl.cppType, varDecl.name, varDecl.storage === "const", isRef)} : ${this.expressionRenderer.render(statement.iterable, undefined, knownVariableTypes)})`;
316
+ }
317
+ return `for (auto item : ${this.expressionRenderer.render(statement.iterable, undefined, knownVariableTypes)})`;
318
+ }
319
+ if (statement.kind === "for_in") {
320
+ if (statement.keys && statement.keys.length > 0) {
321
+ const keysArr = statement.keys.map(k => `"${k}"`).join(", ");
322
+ const objName = statement.object.kind === "identifier" ? statement.object.value : "_obj";
323
+ const idxVar = `_ki_${objName}`;
324
+ this.expressionRenderer.pushPrelude([
325
+ `const char* ${idxVar}_keys[] = { ${keysArr} };`,
326
+ ]);
327
+ return `for (${this.strategy.defaultNumericType()} ${idxVar} = 0; ${idxVar} < ${statement.keys.length}; ${idxVar}++)`;
328
+ }
329
+ const varDecl = statement.variable;
330
+ if (varDecl.kind === "var_decl") {
331
+ const isRef = !isPrimitiveCppType(varDecl.cppType) && !isIndirectType(varDecl.cppType, this.strategy);
332
+ return `for (${this.renderTypedName(varDecl.cppType, varDecl.name, varDecl.storage === "const", isRef)} : ${this.expressionRenderer.render(statement.object, undefined, knownVariableTypes)})`;
333
+ }
334
+ return `for (auto key : ${this.expressionRenderer.render(statement.object, undefined, knownVariableTypes)})`;
335
+ }
336
+ if (statement.kind === "break") {
337
+ return statement.label ? `goto __break_${statement.label};` : "break;";
338
+ }
339
+ if (statement.kind === "continue") {
340
+ return statement.label ? `continue /* ${statement.label}: labeled continue uses normal continue in C++ */;` : "continue;";
341
+ }
342
+ if (statement.kind === "do_while") {
343
+ return `do`;
344
+ }
345
+ if (statement.kind === "switch") {
346
+ const discExpr = statement.expression;
347
+ const discText = this.expressionRenderer.render(discExpr, undefined, knownVariableTypes);
348
+ // C++ switch requires an integral discriminant. Since `number` vars are now
349
+ // `double`/`float` (Issue 5), cast floating-point discriminants to int.
350
+ let needsIntCast = false;
351
+ if (discExpr.kind === "identifier") {
352
+ const t = knownVariableTypes?.get(discExpr.value)?.cppType;
353
+ needsIntCast = t === "double" || t === "float" || t === "long double";
354
+ }
355
+ else if (discExpr.kind === "number" && !Number.isInteger(discExpr.value)) {
356
+ needsIntCast = true;
357
+ }
358
+ return `switch (${needsIntCast ? `static_cast<int>(${discText})` : discText})`;
359
+ }
360
+ if (statement.kind === "try") {
361
+ return "try";
362
+ }
363
+ if (statement.kind === "throw") {
364
+ return this.strategy.renderThrow(this.expressionRenderer.render(statement.value, undefined, knownVariableTypes));
365
+ }
366
+ if (statement.kind === "labeled") {
367
+ // The user's TS label is documentary only — actual control flow
368
+ // uses `goto __break_<label>`. Emitting it as a bare C++ label
369
+ // (`label:`) triggers -Wunused-label because no goto targets it
370
+ // directly. Emit as a comment to preserve the source mapping
371
+ // without the warning.
372
+ return `/* ${statement.label}: */`;
373
+ }
374
+ if (statement.kind === "yield") {
375
+ if (statement.isDelegate && statement.value) {
376
+ const val = this.expressionRenderer.render(statement.value, undefined, knownVariableTypes);
377
+ return `for (auto& __elem : ${val}) co_yield __elem;`;
378
+ }
379
+ if (statement.value) {
380
+ return `co_yield ${this.expressionRenderer.render(statement.value, undefined, knownVariableTypes)};`;
381
+ }
382
+ return "co_yield;";
383
+ }
384
+ if (statement.kind === "block") {
385
+ return `{`;
386
+ }
387
+ if (statement.kind === "hal-op") {
388
+ const resolved = routeHALOp(statement.operation, this.strategy);
389
+ if (resolved?.code) {
390
+ // Strip leading 'return ' from raw hal-op code when emitted as a
391
+ // standalone statement. The HAL definition includes `return` because
392
+ // the TypeScript stub returns a value, but the C++ statement context
393
+ // (e.g. inside void setup()) does not expect it.
394
+ let code = resolved.code;
395
+ if (code.startsWith('return '))
396
+ code = code.slice('return '.length);
397
+ // A raw hal-op statement (e.g. `Async.sleep(10)` →
398
+ // `__cuttlefish_async_sleep(10)`) is a complete C++ statement and
399
+ // needs a terminating semicolon, unless it already ends with one or
400
+ // with `}` (a compound block).
401
+ if (!forHeader && !code.endsWith(';') && !code.endsWith('}')) {
402
+ code = code + ';';
403
+ }
404
+ return code;
405
+ }
406
+ if (resolved?.expression) {
407
+ return forHeader ? resolved.expression : `${resolved.expression};`;
408
+ }
409
+ // Unregistered HAL op: surface as a warning so the user sees it, but
410
+ // keep HAL as an extensibility point. The bare comment is retained as
411
+ // a visual marker in the generated C++.
412
+ this._diagnostics.push({
413
+ severity: "warning",
414
+ code: "TS2CPP_UNHANDLED_HAL",
415
+ message: `HAL operation '${statement.operation.operation}' is not registered with the platform strategy; emitting a placeholder comment.`,
416
+ });
417
+ return `/* unhandled hal-op: ${statement.operation.operation} */`;
418
+ }
419
+ if (statement.kind !== "var_decl") {
420
+ // A StatementIR kind the renderer doesn't know how to render is a
421
+ // transpiler bug — the renderer should cover every kind the IR
422
+ // statement builders produce. This arm is unreachable in the normal
423
+ // transpileFile path (IR lowering marks unsupported statements as
424
+ // errors and the build aborts before emit). Throw so any path that
425
+ // does reach here fails loudly instead of emitting
426
+ // "/* unsupported_statement */" into the C++.
427
+ const kind = statement.kind ?? "<unknown>";
428
+ throw new Error(`StatementRenderer: unsupported StatementIR kind '${kind}' reached emission. ` +
429
+ `This is a transpiler bug; the renderer is missing a case for this kind.`);
430
+ }
431
+ return this.renderVarDecl(statement, forHeader, calleeTransformer, knownVariableTypes);
432
+ })();
433
+ return normalizeRawExpression(rendered, this.strategy, this.classNameMap);
434
+ }
435
+ /**
436
+ * Arrow the leading receiver of an assign/update target when it is a
437
+ * module-scope (file-global) pointer variable: `btn.lastPress` →
438
+ * `btn->lastPress`. This covers ISR-captured globals whose pointer type
439
+ * wasn't visible at IR build time (the target is a plain string here, not a
440
+ * property-access IR node). Only the leading receiver is rewritten — a
441
+ * chain like `btn->field.x` is left alone. Replaces the file-wide text sweep
442
+ * formerly in output-finalizer.ts.
443
+ */
444
+ arrowGlobalPointerTarget(target) {
445
+ if (!this.globalPointerVarTypes || this.globalPointerVarTypes.size === 0)
446
+ return target;
447
+ const m = target.match(/^([A-Za-z_$][\w$]*)\./);
448
+ if (m && this.globalPointerVarTypes.has(m[1])) {
449
+ return target.replace(/^([A-Za-z_$][\w$]*)\./, "$1->");
450
+ }
451
+ return target;
452
+ }
453
+ fixCrossModuleMethodCall(callee) {
454
+ const lastDot = callee.lastIndexOf(".");
455
+ if (lastDot === -1) {
456
+ return callee;
457
+ }
458
+ const receiverCallee = callee.slice(0, lastDot);
459
+ const memberName = callee.slice(lastDot + 1);
460
+ const receiverMatch = receiverCallee.match(/^([A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*)\s*\(/);
461
+ if (!receiverMatch) {
462
+ return callee;
463
+ }
464
+ const receiverCallName = receiverMatch[1];
465
+ const receiverClassName = receiverCallName.split(".")[0];
466
+ const hasKnown = this.knownFunctionReturnTypes?.has(receiverCallName);
467
+ const returnType = this.knownFunctionReturnTypes?.get(receiverCallName);
468
+ const isCrossModuleClass = this.crossModuleClassNames?.has(receiverClassName);
469
+ if (!hasKnown && !isCrossModuleClass) {
470
+ return callee;
471
+ }
472
+ const callPrefix = receiverCallee.replace(new RegExp(`^${receiverCallName.replace(/[.*+?^${}()|[\\]\\]/g, "\\$&")}\\s*\\(`), `${receiverCallName.replace(/\./g, "::")}(`);
473
+ if ((returnType && this.strategy.isPointerType(returnType)) || isCrossModuleClass) {
474
+ return `${callPrefix}->${memberName}`;
475
+ }
476
+ return `${callPrefix}.${memberName}`;
477
+ }
478
+ renderCall(statement, forHeader, calleeTransformer, knownVariableTypes) {
479
+ // HAL bus ownership markers (take/release) — compile-time only, no C++ emission.
480
+ if (statement.args.length === 0 && /\.(take|release)$/.test(statement.callee)) {
481
+ return "";
482
+ }
483
+ // Handle raw statements from setupInitCode
484
+ if (statement.callee.startsWith('__RAW_STMT__')) {
485
+ const rawStmt = statement.callee.slice('__RAW_STMT__'.length);
486
+ return forHeader ? rawStmt : `${rawStmt.endsWith(';') ? rawStmt : rawStmt + ';'}`;
487
+ }
488
+ // Handle emit() — compile-time C++ injection
489
+ if (statement.callee === "__EMIT__") {
490
+ const rawText = statement.args.map(arg => this.renderEmitArg(arg)).join("");
491
+ return forHeader ? rawText : `${rawText.endsWith(';') ? rawText : rawText + ';'}`;
492
+ }
493
+ // Handle console.* calls specially
494
+ if (isConsoleCall(statement.callee)) {
495
+ return this.transformConsoleCall(statement.callee, statement.args, forHeader, knownVariableTypes);
496
+ }
497
+ let callee = statement.callee;
498
+ if (callee.startsWith("this.")) {
499
+ callee = `this->${callee.slice("this.".length)}`;
500
+ }
501
+ if (calleeTransformer) {
502
+ callee = calleeTransformer(callee);
503
+ }
504
+ // Apply the strategy's user-function rename to BARE free-function callees
505
+ // (no `.`/`->`/`::` — those are method/qualified calls, not free fns). This
506
+ // is the single chokepoint through which every call statement is rendered
507
+ // (function bodies, class methods, AND top-level executables spliced into
508
+ // the auto-generated entrypoint), so a rename like Arduino's `main` →
509
+ // `cuttlefish_main` propagates to every call site uniformly, including the
510
+ // `main()` call a top-level executable flows into `setup()`. Routed through
511
+ // mapFunctionName (which the entrypoint sentinel and the strategy both
512
+ // weigh in on), so it stays a no-op on targets with no rename.
513
+ if (!callee.includes('.') && !callee.includes("->") && !callee.includes("::")) {
514
+ callee = this.mapFunctionName(callee);
515
+ }
516
+ callee = this.fixCrossModuleMethodCall(callee);
517
+ callee = this.arrowGlobalPointerTarget(callee);
518
+ const renderedArgs = statement.args.map((arg) => this.expressionRenderer.render(arg, undefined, knownVariableTypes)).join(", ");
519
+ return forHeader ? `${callee}(${renderedArgs})` : `${callee}(${renderedArgs});`;
520
+ }
521
+ renderVarDecl(statement, forHeader, calleeTransformer, knownVariableTypes) {
522
+ // Register this declaration's type in the active scope so that downstream
523
+ // expression rendering (e.g. `arr.length` → sizeof for C arrays) can look
524
+ // up the variable's C++ type. Without this, locals in top-level/generated
525
+ // functions aren't visible to renderPropertyAccess.
526
+ if (knownVariableTypes && statement.cppType) {
527
+ knownVariableTypes.set(statement.name, { cppType: statement.cppType });
528
+ }
529
+ const declaredType = this.normalizeCppType(statement.cppType);
530
+ const volatilePrefix = statement.isVolatile ? "volatile " : "";
531
+ // Transform type name for Arduino library classes (add namespace prefix)
532
+ const transformedType = transformTypeName(statement.cppType, this.classNameMap);
533
+ const ownershipKind = statement.ownershipKind;
534
+ // Emit const for Shared<T> ownership annotations (ownershipKind === 'shared')
535
+ const isConst = statement.storage === "const" || ownershipKind === 'shared';
536
+ // Emit C++ reference for non-primitive Shared<T>/Mutable<T> from named variables.
537
+ // Primitives pass by value (no overhead). Temporary/literal initializers fall back to copy.
538
+ const isRef = (ownershipKind === 'shared' || ownershipKind === 'mutable')
539
+ && !isPrimitiveCppType(statement.cppType)
540
+ && !isIndirectType(statement.cppType, this.strategy)
541
+ && statement.initializer?.kind === 'identifier';
542
+ const declaration = `${volatilePrefix}${this.renderTypedName(transformedType, statement.name, isConst, isRef)}`;
543
+ if (statement.initializer) {
544
+ // Handle lambda initializers: const fn = (x) => expr
545
+ if (statement.initializer.kind === "lambda") {
546
+ const params = statement.initializer.params.map(p => `${p.cppType} ${p.name}`).join(", ");
547
+ const ret = statement.initializer.returnType && statement.initializer.returnType !== "auto"
548
+ ? ` -> ${statement.initializer.returnType}` : "";
549
+ const bodyStr = statement.initializer.body.map(s => " " + this.render(s, false, calleeTransformer, knownVariableTypes)).join("\n");
550
+ const safeName = escapeCppKeyword(statement.name, this.strategy.reservedNames());
551
+ return `auto ${safeName} = [&](${params})${ret} {\n${bodyStr}\n};`;
552
+ }
553
+ // Handle array initializers
554
+ if (statement.initializer.kind === "array") {
555
+ const safeArrName = escapeCppKeyword(statement.name, this.strategy.reservedNames());
556
+ const rawType = statement.cppType;
557
+ const hasObjectElements = statement.initializer.elements.some(e => e.kind === "object");
558
+ if (hasObjectElements) {
559
+ // Check if the declared array type's element is a known named type
560
+ // (e.g. `const pts: Point[]` → std::vector<Point>). If so, use Point
561
+ // directly instead of generating a shadow `_{name}_t` struct (which
562
+ // collides when the same shape appears at multiple sites — demo #11
563
+ // Finding D).
564
+ let elementTypeName = null;
565
+ // Element type of std::vector<T> or T[], detected structurally.
566
+ const elemType = parsedElementString(rawType) ?? null;
567
+ if (elemType && /^[A-Z]/.test(elemType)) {
568
+ elementTypeName = elemType;
569
+ }
570
+ if (elementTypeName) {
571
+ // Use the named element type directly — no shadow struct needed.
572
+ const elements = statement.initializer.elements.map((e) => {
573
+ if (e.kind === "object") {
574
+ const initValues = e.fields.map((f) => {
575
+ const val = this.expressionRenderer.render(f.value, undefined);
576
+ return val;
577
+ }).join(", ");
578
+ return `{ ${initValues} }`;
579
+ }
580
+ return this.expressionRenderer.render(e, undefined);
581
+ });
582
+ const vecType = `std::vector<${elementTypeName}>`;
583
+ const safeArrName2 = escapeCppKeyword(statement.name, this.strategy.reservedNames());
584
+ const constPrefix2 = isConst ? "const " : "";
585
+ return `${constPrefix2}${vecType} ${safeArrName2} = { ${elements} };`;
586
+ }
587
+ const structName = `_${statement.name}_t`;
588
+ // Compile-time-only namespace values (e.g. the `ui` authoring handle
589
+ // from @typecad/ui) are intercepted at IR-build time; their calls
590
+ // lower to IR but the binding itself must emit nothing. Skip the
591
+ // struct synthesis + initializer for these.
592
+ if (activeNamespaceNames.has(statement.name)) {
593
+ return "";
594
+ }
595
+ const firstObj = statement.initializer.elements.find(e => e.kind === "object");
596
+ const nestedStructs = collectNestedStructDefs(firstObj, statement.name, this.pointerVarTypes, this.knownFunctionReturnTypes);
597
+ if (nestedStructs.length > 0) {
598
+ for (const nested of nestedStructs) {
599
+ this.interfaceFieldTypes.set(nested.structName, new Map(nested.fields.map((field) => [field.name, field.type])));
600
+ }
601
+ const nestedDefs = nestedStructs.map((ns) => `struct ${ns.structName} { ${ns.fields.map((f) => `${f.type} ${f.name};`).join(" ")} };`);
602
+ this.expressionRenderer.pushPrelude(nestedDefs);
603
+ }
604
+ const fieldTypes = new Map(firstObj.fields.map((field) => [
605
+ field.name,
606
+ this.inferFieldType(field.value, statement.name, field.name),
607
+ ]));
608
+ this.interfaceFieldTypes.set(structName, fieldTypes);
609
+ const fieldDefs = firstObj.fields
610
+ .map((f) => `${fieldTypes.get(f.name)} ${f.name};`)
611
+ .join(" ");
612
+ this.expressionRenderer.pushPrelude([`struct ${structName} { ${fieldDefs} };`]);
613
+ const streamFields = firstObj.fields
614
+ .map((f) => `"${f.name}: " << obj.${f.name}`)
615
+ .join(' << ", " << ');
616
+ this.expressionRenderer.pushPrelude([
617
+ `std::ostream& operator<<(std::ostream& os, const ${structName}& obj) { os << ${streamFields}; return os; }`
618
+ ]);
619
+ const elements = statement.initializer.elements.map((e) => {
620
+ if (e.kind === "object") {
621
+ const initValues = e.fields.map((f) => {
622
+ const renderExpr = (expr) => this.expressionRenderer.render(expr, calleeTransformer, knownVariableTypes);
623
+ const overridden = this.strategy.objectFieldInitializer(f.value, renderExpr);
624
+ if (overridden !== undefined)
625
+ return overridden;
626
+ return renderExpr(f.value);
627
+ }).join(", ");
628
+ return `{ ${initValues} }`;
629
+ }
630
+ return this.expressionRenderer.render(e, undefined, knownVariableTypes);
631
+ }).join(", ");
632
+ const vecType = `std::vector<${structName}>`;
633
+ return forHeader
634
+ ? `${vecType} ${safeArrName} = { ${elements} }`
635
+ : `${vecType} ${safeArrName} = { ${elements} };`;
636
+ }
637
+ const elements = statement.initializer.elements.map((e) => this.expressionRenderer.render(e, undefined, knownVariableTypes)).join(", ");
638
+ if (this.strategy.needsStdVector() && parsedIsVector(rawType)) {
639
+ return forHeader
640
+ ? `${declaration} = { ${elements} }`
641
+ : `${declaration} = { ${elements} };`;
642
+ }
643
+ if (!this.strategy.needsStdVector() && parsedIsVector(rawType)) {
644
+ // Non-mutable arrays annotated as Array<T> or ReadonlyArray<T> on platforms
645
+ // that don't support std::vector → emit as a plain C-style array.
646
+ // Mutable arrays (.push/.pop/.indexOf) are rewritten to StaticArray<int> in
647
+ // the IR builder (via mutableArrayVars) and never reach this branch.
648
+ const elementType = parsedElementString(rawType) ?? this.strategy.defaultNumericType();
649
+ return forHeader
650
+ ? `${elementType} ${safeArrName}[] = { ${elements} }`
651
+ : `${elementType} ${safeArrName}[] = { ${elements} };`;
652
+ }
653
+ // Use "int" for "auto" element type since C arrays need explicit types
654
+ const arrayType = statement.initializer.elementType === "auto" ? this.strategy.defaultNumericType() : statement.initializer.elementType;
655
+ return forHeader
656
+ ? `${arrayType} ${safeArrName}[] = { ${elements} }`
657
+ : `${arrayType} ${safeArrName}[] = { ${elements} };`;
658
+ }
659
+ // Handle object initializers with inline struct definition
660
+ if (statement.initializer.kind === "object") {
661
+ const placeholderStructName = `_${statement.name}_t`;
662
+ // When the source annotation names a concrete type (e.g. an exported
663
+ // interface referenced across modules: `const cfg: ThresholdConfig = {...}`),
664
+ // cppType already carries that interface/struct name. The struct is declared
665
+ // elsewhere (the interface declaration), so we must NOT emit an inline
666
+ // `struct _name_t {...}` here — that would conflict with the real type.
667
+ // Only synthesize when no explicit named type was provided.
668
+ const declaredCppType = statement.cppType;
669
+ const hasExplicitNamedType = !!declaredCppType &&
670
+ declaredCppType !== "auto" &&
671
+ declaredCppType !== placeholderStructName &&
672
+ parsedIsPlainStructType(declaredCppType);
673
+ const fieldTypes = new Map(statement.initializer.fields.map((field) => [
674
+ field.name,
675
+ this.inferFieldType(field.value, statement.name, field.name),
676
+ ]));
677
+ const initValues = statement.initializer.fields
678
+ .map((f) => {
679
+ const renderExpr = (e) => this.expressionRenderer.render(e, calleeTransformer, knownVariableTypes);
680
+ const overridden = this.strategy.objectFieldInitializer(f.value, renderExpr);
681
+ if (overridden !== undefined)
682
+ return overridden;
683
+ return renderExpr(f.value);
684
+ })
685
+ .join(", ");
686
+ const safeObjName = escapeCppKeyword(statement.name, this.strategy.reservedNames());
687
+ if (hasExplicitNamedType) {
688
+ // Emit the definition using the declared named type; no inline struct.
689
+ // const-ness follows the declared storage so it matches the extern.
690
+ const namedType = this.normalizeCppType(declaredCppType);
691
+ const isConst = statement.storage === "const";
692
+ const constPrefix = isConst ? "const " : "";
693
+ // Demo #18 Finding C: do NOT overwrite an existing authoritative
694
+ // field-type map for this named type (registered in setup.ts from the
695
+ // interface/class declaration). The inferred fieldTypes here come from
696
+ // the initializer VALUES and, after strategy normalization, can be
697
+ // wider/incorrect (e.g. int32_t + name -> all "long long" under the
698
+ // native strategy). Clobbering the correct declared types then makes
699
+ // downstream snprintf format inference pick wrong specifiers
700
+ // (e.g. "%lld" + missing .c_str() for a std::string field). Only seed
701
+ // the map when no declared entry exists (e.g. an anonymous struct).
702
+ if (!this.interfaceFieldTypes.has(namedType)) {
703
+ this.interfaceFieldTypes.set(namedType, fieldTypes);
704
+ }
705
+ return forHeader
706
+ ? `${constPrefix}${namedType} ${safeObjName} = { ${initValues} }`
707
+ : `${constPrefix}${namedType} ${safeObjName} = { ${initValues} };`;
708
+ }
709
+ const structName = placeholderStructName;
710
+ // Collect nested struct definitions (deepest first) and emit as prelude
711
+ const nestedStructs = collectNestedStructDefs(statement.initializer, statement.name, this.pointerVarTypes, this.knownFunctionReturnTypes);
712
+ if (nestedStructs.length > 0) {
713
+ for (const nested of nestedStructs) {
714
+ this.interfaceFieldTypes.set(nested.structName, new Map(nested.fields.map((field) => [field.name, field.type])));
715
+ }
716
+ const nestedDefs = nestedStructs.map((ns) => `struct ${ns.structName} { ${ns.fields.map((f) => `${f.type} ${f.name};`).join(" ")} };`);
717
+ this.expressionRenderer.pushPrelude(nestedDefs);
718
+ }
719
+ this.interfaceFieldTypes.set(structName, fieldTypes);
720
+ const fieldDefs = statement.initializer.fields
721
+ .map((f) => `${fieldTypes.get(f.name)} ${f.name};`)
722
+ .join(" ");
723
+ return forHeader
724
+ ? `struct ${structName} { ${fieldDefs} } ${safeObjName} = { ${initValues} }`
725
+ : `struct ${structName} { ${fieldDefs} } ${safeObjName} = { ${initValues} };`;
726
+ }
727
+ // Handle spread array initializers
728
+ if (statement.initializer.kind === "spread_array") {
729
+ let arrayType = statement.initializer.elementType === "auto" ? this.strategy.defaultNumericType() : statement.initializer.elementType;
730
+ const spreadName = this.expressionRenderer.render(statement.initializer.spreadExpr, calleeTransformer, knownVariableTypes);
731
+ if (statement.initializer.elementType === "auto" && knownVariableTypes && statement.initializer.spreadExpr.kind === "identifier") {
732
+ const srcInfo = knownVariableTypes.get(statement.initializer.spreadExpr.value);
733
+ if (srcInfo) {
734
+ const vectorElem = parsedElementString(srcInfo.cppType);
735
+ if (vectorElem) {
736
+ arrayType = vectorElem;
737
+ }
738
+ }
739
+ }
740
+ const renderedExtraElements = statement.initializer.additionalElements
741
+ .map(e => this.expressionRenderer.render(e, calleeTransformer, knownVariableTypes));
742
+ const safeSpreadArrName = escapeCppKeyword(statement.name, this.strategy.reservedNames());
743
+ if (this.strategy.needsStdVector()) {
744
+ const parts = [`std::vector<${arrayType}> ${safeSpreadArrName}(${spreadName})`];
745
+ for (const elem of renderedExtraElements) {
746
+ parts.push(`${safeSpreadArrName}.push_back(${elem})`);
747
+ }
748
+ return forHeader
749
+ ? parts[0]
750
+ : parts.join("; ") + ";";
751
+ }
752
+ const initializerParts = [`/* spread from ${spreadName} */`, ...renderedExtraElements];
753
+ const initializerText = initializerParts.join(', ');
754
+ return forHeader
755
+ ? `${arrayType} ${safeSpreadArrName}[] = { ${initializerText} }`
756
+ : `${arrayType} ${safeSpreadArrName}[] = { ${initializerText} };`;
757
+ }
758
+ // Enum ↔ integral storage boundary (SUPPORT_MATRIX §1.7). TS lets an
759
+ // enum and a number flow into each other freely (enums ARE numbers at
760
+ // runtime), but the lowered C++ `enum class` has NO implicit conversion
761
+ // in EITHER direction: `const n: int = Color.Red` AND
762
+ // `const c: Color = intCell` both fail. Route the initializer through
763
+ // the shared target-type-aware helper so both directions lower with the
764
+ // right `static_cast`, replacing the prior point-specific enum→int-only
765
+ // inline check. Demo #32 Finding A.
766
+ const finalInit = this.expressionRenderer.renderValueForTarget(statement.initializer, declaredType, knownVariableTypes, calleeTransformer);
767
+ return forHeader
768
+ ? `${declaration} = ${finalInit}`
769
+ : `${declaration} = ${finalInit};`;
770
+ }
771
+ return forHeader ? declaration : `${declaration};`;
772
+ }
773
+ /**
774
+ * Renders a typed name with proper C++ syntax.
775
+ */
776
+ renderTypedName(cppType, name, isConst = false, isRef = false) {
777
+ const safeName = escapeCppKeyword(name, this.strategy.reservedNames());
778
+ const normalizedType = this.normalizeCppType(cppType);
779
+ const fnPtrMatch = normalizedType.match(/^(.+?)\s*\(\*\)\((.*)\)$/);
780
+ if (fnPtrMatch) {
781
+ const returnType = fnPtrMatch[1].trim();
782
+ const params = fnPtrMatch[2].trim();
783
+ const constPrefix = isConst ? "const " : "";
784
+ return `${constPrefix}${returnType} (*${safeName})(${params})`;
785
+ }
786
+ const alreadyConstQualified = /^const\s+/.test(normalizedType);
787
+ const arrayMatch = normalizedType.match(/^(.+?)\s*\[(\d*)\]$/);
788
+ if (arrayMatch) {
789
+ const baseType = arrayMatch[1].trim();
790
+ const size = arrayMatch[2];
791
+ const constPrefix = isConst && !alreadyConstQualified ? "const " : "";
792
+ return `${constPrefix}${baseType} ${safeName}[${size}]`;
793
+ }
794
+ if (isConst && !alreadyConstQualified && parsedIsPointer(normalizedType)) {
795
+ return `${normalizedType} ${safeName}`;
796
+ }
797
+ const constPrefix = isConst && !alreadyConstQualified ? "const " : "";
798
+ const refMark = isRef ? "& " : " ";
799
+ return `${constPrefix}${normalizedType}${refMark}${safeName}`;
800
+ }
801
+ /**
802
+ * Renders function parameters.
803
+ * Emits `const` for parameters annotated with `Shared<T>` (ownershipKind === 'shared').
804
+ */
805
+ renderParameters(parameters, forHeader = false) {
806
+ if (parameters.length === 0) {
807
+ return "";
808
+ }
809
+ return parameters
810
+ .map((parameter) => {
811
+ const paramOwnershipKind = parameter.ownershipKind;
812
+ const hasOwnership = paramOwnershipKind === 'shared' || paramOwnershipKind === 'mutable';
813
+ const isNonPrimitiveNonPointer = !isPrimitiveCppType(parameter.cppType)
814
+ && !isIndirectType(parameter.cppType, this.strategy);
815
+ const paramTypeName = parameter.cppType.trim();
816
+ // User-defined struct/interface types (not std:: containers, not enums)
817
+ // default to pass-by-const-reference to avoid expensive copies and surface
818
+ // accidental local mutations as errors. Shared<T>/Mutable<T> keep borrowing
819
+ // any non-primitive, non-pointer type (including std:: containers).
820
+ const isUserStructType = isNonPrimitiveNonPointer
821
+ && !parsedIsStdContainer(paramTypeName)
822
+ && !this.enumNames.has(paramTypeName);
823
+ const isRef = (hasOwnership && isNonPrimitiveNonPointer) || isUserStructType;
824
+ // Refs default to a const borrow; Mutable<T> unlocks a mutable reference.
825
+ // Primitives only gain const when explicitly annotated with Shared<T>.
826
+ const isConst = isRef
827
+ ? (!hasOwnership || paramOwnershipKind === 'shared')
828
+ : paramOwnershipKind === 'shared';
829
+ let cppType = parameter.cppType;
830
+ if (parameter.isRest) {
831
+ const elementType = cppType.replace(/^std::vector<(.+)>$/, '$1') || 'auto';
832
+ cppType = `const std::vector<${elementType}>&`;
833
+ const safeName = escapeCppKeyword(parameter.name, this.strategy.reservedNames());
834
+ let result = `${cppType} ${safeName}`;
835
+ if (forHeader && parameter.defaultValue) {
836
+ result += ` = ${this.expressionRenderer.render(parameter.defaultValue)}`;
837
+ }
838
+ return result;
839
+ }
840
+ let result = this.renderTypedName(cppType, parameter.name, isConst, isRef);
841
+ if (forHeader && parameter.defaultValue) {
842
+ result += ` = ${this.expressionRenderer.render(parameter.defaultValue)}`;
843
+ }
844
+ return result;
845
+ })
846
+ .join(", ");
847
+ }
848
+ /**
849
+ * Public type-mapping entry point for emitters that emit types directly
850
+ * (function return types, forward declarations) rather than via
851
+ * renderTypedName. Applies the same string-enum → const char* substitution.
852
+ */
853
+ mapTypeForEmit(typeName) {
854
+ return this.normalizeCppType(typeName);
855
+ }
856
+ /**
857
+ * Map a function's return type for emission. Unlike {@link mapTypeForEmit},
858
+ * this respects the strategy's {@code mapReturnType} contract: the value
859
+ * produced by {@code mapReturnType} is already final and must NOT be
860
+ * re-normalised. This matters for entrypoints like {@code main}, whose
861
+ * mapped {@code int} return type would otherwise be turned back into
862
+ * {@code long long} by {@code normalizeCppType}.
863
+ *
864
+ * For non-entrypoint functions we still apply the string-enum → const char*
865
+ * substitution (which {@code mapReturnType} does not perform), preserving
866
+ * existing behaviour for ordinary functions.
867
+ */
868
+ mapReturnTypeForEmit(fnName, returnType) {
869
+ if (fnName === this.strategy.entrypointFunctionName()) {
870
+ return returnType;
871
+ }
872
+ return this.mapTypeForEmit(returnType);
873
+ }
874
+ normalizeCppType(typeName) {
875
+ // A TS string-enum type name (e.g. `Color`) is lowered to a namespace of
876
+ // constexpr const char* constants, so values of that type are const char*.
877
+ // Substitute the type so declarations like `const Color c` become
878
+ // `const const char* c` (valid: const pointer to const char).
879
+ if (this.stringEnumNames.has(typeName)) {
880
+ return this.strategy.normalizeCppType("const char*");
881
+ }
882
+ return this.strategy.normalizeCppType(typeName);
883
+ }
884
+ inferFieldType(value, parentName, fieldName) {
885
+ return inferObjectFieldType(value, this.pointerVarTypes, this.knownFunctionReturnTypes, undefined, undefined, undefined, // largeEnumNames - would need to pass through
886
+ parentName, fieldName, this.strategy.defaultNumericType(), (o, n) => this.strategy.resolvePinType?.(o, n));
887
+ }
888
+ /**
889
+ * Transform console.log/error/warn calls based on target platform.
890
+ */
891
+ transformConsoleCall(callee, args, forHeader, knownVariableTypes) {
892
+ const method = getConsoleMethod(callee);
893
+ const renderedArgs = args.map((arg) => {
894
+ let rendered = this.expressionRenderer.render(arg, undefined, knownVariableTypes);
895
+ if (arg.kind === "identifier" && knownVariableTypes) {
896
+ const varInfo = knownVariableTypes.get(arg.value);
897
+ if (varInfo && this.enumNames.has(varInfo.cppType)) {
898
+ rendered = `static_cast<int>(${rendered})`;
899
+ }
900
+ }
901
+ if (arg.kind === "property-access" && arg.isEnum) {
902
+ rendered = `static_cast<int>(${rendered})`;
903
+ }
904
+ return rendered;
905
+ }).join(" << ");
906
+ return this.strategy.transformConsoleCall(method, renderedArgs, forHeader);
907
+ }
908
+ renderEmitArg(arg) {
909
+ if (arg.kind === "string")
910
+ return arg.value;
911
+ if (arg.kind === "string_concat")
912
+ return arg.parts.map(p => this.renderEmitArg(p)).join("");
913
+ // For template_string inside emit(), the inner expression is a
914
+ // string_concat of literal parts and interpolated identifiers. We render
915
+ // each part directly as C++ text — string literals contribute their raw
916
+ // text, identifiers contribute their variable name — bypassing the
917
+ // snprintf machinery that normal string rendering would use.
918
+ if (arg.kind === "template_string")
919
+ return this.renderEmitArg(arg.expression);
920
+ return this.expressionRenderer.render(arg);
921
+ }
922
+ /**
923
+ * Map a function name to its platform-specific name.
924
+ *
925
+ * The entrypoint sentinel `__cuttlefish_entrypoint__` maps to the strategy's
926
+ * entrypoint name (`main` on native, `setup` on Arduino). For every OTHER
927
+ * name we delegate to `strategy.mapFunctionName`, which is where a target
928
+ * can rename a user function that would otherwise collide with a C++/
929
+ * framework reserved name — e.g. the Arduino strategy renames a user
930
+ * `function main()` to `cuttlefish_main`, because Arduino has no `main()`
931
+ * (the entrypoints are the auto-generated `setup()`/`loop()`), and a
932
+ * file-scope `static void main()` collides with C++'s required `int main()`
933
+ * signature. Routing every name through the strategy here is the single
934
+ * place that makes such renames actually take effect at emit time.
935
+ */
936
+ mapFunctionName(originalName) {
937
+ if (originalName === "__cuttlefish_entrypoint__") {
938
+ return this.strategy.entrypointFunctionName();
939
+ }
940
+ return this.strategy.mapFunctionName(originalName);
941
+ }
942
+ /**
943
+ * Map a TypeScript return type to its C++ equivalent for a specific function.
944
+ */
945
+ mapReturnType(fnName, tsType) {
946
+ return this.strategy.mapReturnType(fnName, tsType);
947
+ }
948
+ }