@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,851 @@
1
+ import path from "node:path";
2
+ import { filterPolyfillHelpers, isStringEnum } from "../../api/shared/index.js";
3
+ import { analyzeProgram } from "../../ir/program-analysis.js";
4
+ import { collectStatementIdentifiers } from "../../ir/identifier-collector.js";
5
+ import { ensureDir } from "../../utils/fs.js";
6
+ import { resolveImport } from "../../libdef/registry.js";
7
+ import { emitPolyfillBoilerplate } from "../native-helpers-emitter.js";
8
+ import { resolveStrategy } from "../../platform/registry.js";
9
+ import { getLoadedFramework } from "../../framework-registry.js";
10
+ import { entryHasUI } from "../../ui-hook.js";
11
+ import { createEmissionScopeState, statementNeedsSnprintf, } from "../snprintf-helpers.js";
12
+ import { ExpressionRenderer } from "../expression-renderer.js";
13
+ import { StatementRenderer } from "../statement-renderer.js";
14
+ import { isCuttlefishSDKImport, normalizeInclude, applySymbolMap, generateAsyncTaskClass, resolveTranspiledModuleInclude, } from "../utils/index.js";
15
+ function resolveTemplateReturnType(returnType, typeParameters) {
16
+ if (!typeParameters || typeParameters.length === 0)
17
+ return returnType;
18
+ return returnType;
19
+ }
20
+ function filterShimBlock(lines, startMarker, endMarker) {
21
+ const startIdx = lines.findIndex(l => l.includes(startMarker));
22
+ if (startIdx === -1)
23
+ return lines;
24
+ const endIdx = lines.findIndex((l, i) => i >= startIdx && l.includes(endMarker));
25
+ if (endIdx === -1)
26
+ return lines;
27
+ const filtered = lines.slice();
28
+ filtered.splice(startIdx, endIdx - startIdx + 1);
29
+ while (filtered.length > 0 && filtered[startIdx] === '') {
30
+ filtered.splice(startIdx, 1);
31
+ }
32
+ return filtered;
33
+ }
34
+ /**
35
+ * Extract a `#ifndef MACRO ... #endif` include-guard block from a shim line
36
+ * list. Used to emit just the macro definition (e.g. CUTTLEFISH_UNDEFINED) in
37
+ * non-entry files of a split compilation, where the full helper shim belongs
38
+ * to the entry file but the macro token is still referenced here.
39
+ * Returns an empty array if no matching guard is found.
40
+ */
41
+ function extractShimMacro(lines, macroName) {
42
+ const ifndefIdx = lines.findIndex(l => {
43
+ const trimmed = l.trim();
44
+ return trimmed.startsWith('#ifndef') && trimmed.endsWith(macroName);
45
+ });
46
+ if (ifndefIdx === -1)
47
+ return [];
48
+ // Find the matching #endif that closes this guard.
49
+ let depth = 1;
50
+ let endIdx = -1;
51
+ for (let i = ifndefIdx + 1; i < lines.length; i++) {
52
+ const trimmed = lines[i].trim();
53
+ if (trimmed.startsWith('#ifndef') || trimmed.startsWith('#ifdef'))
54
+ depth++;
55
+ else if (trimmed.startsWith('#endif')) {
56
+ depth--;
57
+ if (depth === 0) {
58
+ endIdx = i;
59
+ break;
60
+ }
61
+ }
62
+ }
63
+ if (endIdx === -1)
64
+ return [];
65
+ return lines.slice(ifndefIdx, endIdx + 1);
66
+ }
67
+ export function buildEmitterContext(program, options) {
68
+ const largeEnumNames = new Set();
69
+ const enumNames = new Set();
70
+ const stringEnumNames = new Set();
71
+ for (const e of program.enums) {
72
+ enumNames.add(e.name);
73
+ if (isStringEnum(e)) {
74
+ stringEnumNames.add(e.name);
75
+ }
76
+ if (e.members.some(m => typeof m.value === "number" && (m.value > 32767 || m.value < -32768))) {
77
+ largeEnumNames.add(e.name);
78
+ }
79
+ }
80
+ const namespaceNames = new Set();
81
+ for (const ns of program.namespaces) {
82
+ namespaceNames.add(ns.name);
83
+ for (const e of ns.enums) {
84
+ enumNames.add(e.name);
85
+ if (isStringEnum(e)) {
86
+ stringEnumNames.add(e.name);
87
+ }
88
+ if (e.members.some(m => typeof m.value === "number" && (m.value > 32767 || m.value < -32768))) {
89
+ largeEnumNames.add(e.name);
90
+ }
91
+ }
92
+ }
93
+ if (options.crossModuleEnumNames) {
94
+ for (const name of options.crossModuleEnumNames) {
95
+ enumNames.add(name);
96
+ }
97
+ }
98
+ if (options.crossModuleStringEnumNames) {
99
+ for (const name of options.crossModuleStringEnumNames) {
100
+ stringEnumNames.add(name);
101
+ }
102
+ }
103
+ const strategy = options.strategy ?? resolveStrategy(options.target ?? "generic");
104
+ strategy.setLargeEnumNames?.(largeEnumNames);
105
+ const reservedNames = strategy.reservedNames();
106
+ ensureDir(options.outDir);
107
+ const classNameMap = (() => {
108
+ try {
109
+ const framework = getLoadedFramework();
110
+ if (framework.classNameMapBuilder) {
111
+ return framework.classNameMapBuilder(program.imports);
112
+ }
113
+ }
114
+ catch {
115
+ // No loaded framework
116
+ }
117
+ return undefined;
118
+ })();
119
+ const programAnalysis = analyzeProgram(program, strategy);
120
+ if (options.platformContext) {
121
+ options.platformContext.analysis = programAnalysis;
122
+ if (!options.platformContext.architecture && program.boardConstants) {
123
+ options.platformContext.architecture = program.boardConstants.get("architecture");
124
+ }
125
+ }
126
+ const originalBaseName = path.basename(program.fileName).replace(/\.[^.]+$/, "");
127
+ const outDirBaseName = path.basename(path.resolve(options.outDir));
128
+ const baseName = options.npmPackage?.moduleKey
129
+ || strategy.overrideBaseName(originalBaseName, outDirBaseName, options.isEntryFile ?? true, !!options.npmPackage);
130
+ const isNpmPackage = !!options.npmPackage;
131
+ const isEntryFile = options.isEntryFile !== false;
132
+ const isrPrefix = isEntryFile ? 'main' : originalBaseName;
133
+ const effectiveEmitMode = strategy.effectiveEmitMode(options.emitMode, isNpmPackage);
134
+ const includes = [];
135
+ const symbolMap = {};
136
+ let profileDiagnostics = [];
137
+ // Shared sink for emit-time diagnostics (HAL warnings, etc.). Passed by
138
+ // reference to the renderers and merged into the final diagnostics list by
139
+ // finalizeOutput.
140
+ const emitDiagnostics = [];
141
+ let shimLines = [];
142
+ const isEntryFileForPolyfills = isEntryFile;
143
+ const nativePolyfills = isEntryFileForPolyfills
144
+ ? (strategy.generateNativePolyfills?.(program, options.platformContext) ?? [])
145
+ : (strategy.generateNativePolyfills?.(program, options.platformContext) ?? []);
146
+ let filteredNativePolyfills = filterPolyfillHelpers(nativePolyfills, programAnalysis.usedPolyfillHelpers);
147
+ if (!programAnalysis.hasThrowStatements) {
148
+ filteredNativePolyfills = filteredNativePolyfills.filter(p => p.id !== "cuttlefish_halt");
149
+ }
150
+ const usesTimers = programAnalysis.usedPolyfillHelpers.has('__tc_setInterval') ||
151
+ programAnalysis.usedPolyfillHelpers.has('__tc_setTimeout');
152
+ if (!usesTimers) {
153
+ filteredNativePolyfills = filteredNativePolyfills.filter(p => p.id !== "timer_methods");
154
+ }
155
+ const allPolyfills = filteredNativePolyfills;
156
+ const emittedPolyfills = allPolyfills.length > 0
157
+ ? emitPolyfillBoilerplate(allPolyfills)
158
+ : undefined;
159
+ const hasAsyncRuntime = program.functions.some(fn => fn.isAsync);
160
+ const hasPromiseRuntime = nativePolyfills.some((p) => p.id === "async_runtime" && p.hasPromiseRuntime === true);
161
+ if (!isNpmPackage) {
162
+ includes.push(...strategy.forcedIncludes(program, options.platformContext));
163
+ Object.assign(symbolMap, strategy.symbolAliases(program, options.platformContext));
164
+ if (isEntryFile) {
165
+ shimLines = [...strategy.shimLines(program, options.platformContext)];
166
+ }
167
+ else {
168
+ // Non-entry files in split compilation: always emit the full shim
169
+ // block. A class method body that uses `??` lowers to a
170
+ // cuttlefish_nullish(...) call, but the helper lives in the entry
171
+ // file's shim — which isn't visible from this header. We used to gate
172
+ // this on programAnalysis.usesNullish, but that flag is unreliable:
173
+ // the analysis runs before the expression renderer, which can
174
+ // introduce cuttlefish_nullish calls (e.g. for default-param
175
+ // destructuring, or `??` inside class methods on abstract bases)
176
+ // that the analysis pass didn't see, leaving the header referencing
177
+ // an undeclared helper. The shim is fully idempotent (#ifndef
178
+ // guards), so emitting it unconditionally is safe and cheap.
179
+ shimLines = [...strategy.shimLines(program, options.platformContext)];
180
+ }
181
+ if (!programAnalysis.usesStringConversion) {
182
+ shimLines = shimLines.filter(l => !l.includes('std::string String('));
183
+ }
184
+ if (!programAnalysis.usesDateNow) {
185
+ shimLines = shimLines.filter(l => !l.includes('namespace Date'));
186
+ }
187
+ // The UI runtime's per-frame tick uses millis() (injected by the emitter,
188
+ // not authored in user source), so keep the millis() shim when a UI is
189
+ // mounted even if the source-level analysis didn't flag usesMillis.
190
+ if (!programAnalysis.usesMillis && !entryHasUI()) {
191
+ shimLines = shimLines.filter(l => !l.includes('millis()'));
192
+ }
193
+ // Strip the nullish helper FUNCTIONS (not the CUTTLEFISH_UNDEFINED macro)
194
+ // when the file doesn't actually emit cuttlefish_nullish(...) calls. A
195
+ // file that only references `null`/`undefined` literals needs just the
196
+ // macro token, not the function definitions. The usesNullishHelper flag
197
+ // tracks files that lower `??` to cuttlefish_nullish(...) calls.
198
+ //
199
+ // Note: for split-file HEADERS, output-finalizer.ts separately injects
200
+ // the full shim when the header's emitted lines contain an actual
201
+ // cuttlefish_nullish( call (a more reliable post-emit check than the
202
+ // per-file pre-emit analysis flag). The logic here governs the .cpp
203
+ // source shim only.
204
+ if (!programAnalysis.usesNullishHelper) {
205
+ const filtered = [];
206
+ for (let i = 0; i < shimLines.length; i++) {
207
+ const l = shimLines[i];
208
+ if (l.includes('cuttlefish_is_nullish') || l.includes('cuttlefish_exists') || l.includes('cuttlefish_nullish'))
209
+ continue;
210
+ filtered.push(l);
211
+ }
212
+ shimLines = filtered;
213
+ }
214
+ if (!programAnalysis.usesNum) {
215
+ shimLines = filterShimBlock(shimLines, 'struct __tc_Num {', '} Num;');
216
+ }
217
+ if (!programAnalysis.usesTiming) {
218
+ shimLines = filterShimBlock(shimLines, 'struct __tc_Timing {', '} Timing;');
219
+ }
220
+ if (!programAnalysis.usesWDT) {
221
+ shimLines = filterShimBlock(shimLines, '#include <avr/wdt.h>', '} WDT;');
222
+ }
223
+ if (!programAnalysis.usesStrPtr) {
224
+ shimLines = filterShimBlock(shimLines, '#ifndef CUTTLEFISH_STR_BUF_SIZE', 'inline size_t (strlen)(const __tc_str_ptr& s) { return ::strlen(s.buf); }');
225
+ }
226
+ profileDiagnostics = [...strategy.profileDiagnostics(program, options.platformContext)];
227
+ }
228
+ for (const imported of program.imports) {
229
+ // Resolve the default import name (import X from "./mod.js") the same way as
230
+ // named imports — add it to the symbolMap so cross-module references
231
+ // resolve. Demo #12 Finding C — was skipped, so `encode` wasn't declared.
232
+ const defaultName = imported.defaultImportName;
233
+ if (isCuttlefishSDKImport(imported.moduleSpecifier, program.fileName)) {
234
+ for (const symbol of imported.namedImports) {
235
+ symbolMap[symbol] = symbol;
236
+ }
237
+ if (defaultName)
238
+ symbolMap[defaultName] = defaultName;
239
+ continue;
240
+ }
241
+ if (options.nativeModules && options.nativeModules.has(imported.moduleSpecifier)) {
242
+ for (const symbol of imported.namedImports) {
243
+ symbolMap[symbol] = symbol;
244
+ }
245
+ if (defaultName)
246
+ symbolMap[defaultName] = defaultName;
247
+ continue;
248
+ }
249
+ // .ui.html modules: their content is injected directly into the entry TU
250
+ // by emitUIRuntime (static tables + runtime header), so they need NO
251
+ // separate header include. Just register the imported symbols (e.g.
252
+ // `screen`) so cross-module references resolve.
253
+ if (imported.moduleSpecifier.endsWith(".ui.html")) {
254
+ for (const symbol of imported.namedImports) {
255
+ symbolMap[symbol] = symbol;
256
+ }
257
+ if (defaultName)
258
+ symbolMap[defaultName] = defaultName;
259
+ continue;
260
+ }
261
+ // Compile-time-only packages (@typecad/ui, @typecad/ui): their calls are
262
+ // intercepted at IR-build time; the package emits NO C++ module/header.
263
+ // Register the imported symbols so references resolve, but skip the
264
+ // #include generation (there is no Ui.h to include).
265
+ if (imported.moduleSpecifier === "@typecad/ui" || imported.moduleSpecifier === "@typecad/ui") {
266
+ for (const symbol of imported.namedImports) {
267
+ symbolMap[symbol] = symbol;
268
+ }
269
+ if (defaultName)
270
+ symbolMap[defaultName] = defaultName;
271
+ continue;
272
+ }
273
+ const transpiledInclude = resolveTranspiledModuleInclude(imported.moduleSpecifier, options.npmPackages, program.fileName);
274
+ if (transpiledInclude.isTranspiled) {
275
+ includes.push(transpiledInclude.include);
276
+ for (const symbol of imported.namedImports) {
277
+ symbolMap[symbol] = symbol;
278
+ }
279
+ if (defaultName)
280
+ symbolMap[defaultName] = defaultName;
281
+ }
282
+ else {
283
+ const resolved = resolveImport(imported, options.libdefs, options.target, options.platformContext, program.fileName);
284
+ includes.push(normalizeInclude(resolved.include));
285
+ Object.assign(symbolMap, resolved.symbolMap);
286
+ if (defaultName)
287
+ symbolMap[defaultName] = defaultName;
288
+ }
289
+ }
290
+ const templateInterfaceNames = new Set();
291
+ const interfaceNamespaceMap = new Map();
292
+ const interfaceFieldTypes = new Map();
293
+ // Shared map populated as top-level const object literals are emitted
294
+ // (function-emitter-impl.ts) and read by the expression renderer to decide
295
+ // `.` vs `::` member access on those variables.
296
+ const knownTopLevelObjectTypes = new Map();
297
+ // Module-scope pointer variables, populated by runTopLevelPreprocessing
298
+ // (top-level-prep.ts) into THIS map (in place, by reference) so the
299
+ // ExpressionRenderer — constructed below with this same reference — can
300
+ // decide `->` vs `.` for bare identifiers that resolve to file-global
301
+ // pointers (e.g. ISR-captured `const btn = new Button()`). Replaces the
302
+ // file-wide text sweep formerly in output-finalizer.ts.
303
+ const globalPointerVarTypes = new Map();
304
+ for (const iface of program.interfaces) {
305
+ if (iface.parentScope) {
306
+ interfaceNamespaceMap.set(iface.name, iface.parentScope);
307
+ }
308
+ if (iface.fields.length > 0) {
309
+ const fieldTypes = new Map();
310
+ for (const field of iface.fields) {
311
+ fieldTypes.set(field.name, field.cppType);
312
+ }
313
+ interfaceFieldTypes.set(iface.name, fieldTypes);
314
+ for (const field of iface.fields) {
315
+ if (/^[A-Z]$/.test(field.cppType)) {
316
+ templateInterfaceNames.add(iface.name);
317
+ break;
318
+ }
319
+ }
320
+ }
321
+ }
322
+ for (const classDef of program.classes) {
323
+ if (classDef.fields.length > 0 || classDef.extendsClass) {
324
+ const fieldTypes = new Map();
325
+ if (classDef.extendsClass) {
326
+ const parentFields = interfaceFieldTypes.get(classDef.extendsClass);
327
+ if (parentFields) {
328
+ for (const [fname, ftype] of parentFields) {
329
+ fieldTypes.set(fname, ftype);
330
+ }
331
+ }
332
+ }
333
+ for (const field of classDef.fields) {
334
+ fieldTypes.set(field.name, field.cppType);
335
+ }
336
+ interfaceFieldTypes.set(classDef.name, fieldTypes);
337
+ }
338
+ }
339
+ if (options.crossModuleClassFieldTypes) {
340
+ for (const [className, fieldTypes] of options.crossModuleClassFieldTypes) {
341
+ if (!interfaceFieldTypes.has(className)) {
342
+ interfaceFieldTypes.set(className, fieldTypes);
343
+ }
344
+ }
345
+ }
346
+ // Build the set of all known class names (local + cross-module).
347
+ // Class instances are always created with `new`, which returns a pointer;
348
+ // function parameters that reference these types must also be pointers.
349
+ const classNames = new Set();
350
+ for (const cls of program.classes)
351
+ classNames.add(cls.name);
352
+ if (options.crossModuleClasses) {
353
+ for (const name of options.crossModuleClasses)
354
+ classNames.add(name);
355
+ }
356
+ // Map a function's original TS name to its emitted C++ name, applying the
357
+ // strategy's user-function renames. The entrypoint sentinel
358
+ // `__cuttlefish_entrypoint__` maps to the strategy's entrypoint name
359
+ // (`main` on native, `setup` on Arduino); every other name is routed through
360
+ // `strategy.mapFunctionName`, which is where a target renames a user
361
+ // function that would otherwise collide with a C++/framework reserved name
362
+ // — e.g. Arduino renames a user `function main()` to `cuttlefish_main`,
363
+ // because Arduino has no `main()` (the entrypoints are the auto-generated
364
+ // `setup()`/`loop()`), and a file-scope `static void main()` collides with
365
+ // C++'s required `int main()` signature. Call-site renames are applied
366
+ // uniformly in StatementRenderer.renderCall (the single call-rendering
367
+ // chokepoint), so every reference — definition, forward decl, and every call
368
+ // site including the top-level `main()` call spliced into `setup()` —
369
+ // follows the rename.
370
+ const mapEmittedFnName = (originalName) => originalName === "__cuttlefish_entrypoint__"
371
+ ? strategy.entrypointFunctionName()
372
+ : strategy.mapFunctionName(originalName);
373
+ const mappedFunctions = program.functions.map((fn) => {
374
+ const fnName = mapEmittedFnName(fn.originalName);
375
+ return {
376
+ name: fnName,
377
+ returnType: resolveTemplateReturnType(strategy.mapReturnType(fnName, fn.returnType), fn.typeParameters),
378
+ sourceSpan: fn.sourceSpan,
379
+ leadingComments: fn.leadingComments,
380
+ trailingComments: fn.trailingComments,
381
+ parameters: fn.parameters,
382
+ isAsync: fn.isAsync,
383
+ typeParameters: fn.typeParameters,
384
+ typeParameterConstraints: fn.typeParameterConstraints,
385
+ isReadonlyReturnType: fn.isReadonlyReturnType,
386
+ isGenerator: fn.isGenerator,
387
+ isExported: fn.isExported,
388
+ statements: fn.statements.map((stmt) => {
389
+ if (stmt.kind === "call") {
390
+ return { ...stmt, callee: applySymbolMap(stmt.callee, symbolMap) };
391
+ }
392
+ return stmt;
393
+ }),
394
+ };
395
+ });
396
+ const knownFunctionReturnTypes = new Map(options.crossModuleFunctionReturnTypes);
397
+ for (const fn of mappedFunctions) {
398
+ knownFunctionReturnTypes.set(fn.name, fn.returnType);
399
+ }
400
+ for (const fn of program.functions) {
401
+ const fnName = fn.originalName === "__cuttlefish_entrypoint__" ? strategy.entrypointFunctionName() : fn.originalName;
402
+ knownFunctionReturnTypes.set(fn.originalName, strategy.mapReturnType(fnName, fn.returnType));
403
+ }
404
+ // Also register class method return types (keyed by bare method name) so
405
+ // downstream type inference — in particular snprintf format-specifier
406
+ // selection for `this->method()` / `obj->method()` operands in a string
407
+ // concat — can resolve a method's return type. Without this, a
408
+ // double-returning method like `cargoUsed()` defaults to `%d` in the
409
+ // emitted snprintf, which misaligns the format/arg list and corrupts
410
+ // output. The bare-name key is safe here because `inferExpressionCppType`
411
+ // looks up `expr.callee` which is the bare method name for member calls.
412
+ for (const cls of program.classes) {
413
+ for (const m of cls.methods) {
414
+ if (!knownFunctionReturnTypes.has(m.name)) {
415
+ knownFunctionReturnTypes.set(m.name, m.returnType);
416
+ }
417
+ }
418
+ for (const g of cls.getters) {
419
+ if (!knownFunctionReturnTypes.has(g.name)) {
420
+ knownFunctionReturnTypes.set(g.name, g.returnType);
421
+ }
422
+ }
423
+ }
424
+ const snprintfCounter = { value: 0 };
425
+ const topLevelScope = createEmissionScopeState();
426
+ if (options.crossModuleVariableTypes) {
427
+ for (const [name, cppType] of options.crossModuleVariableTypes) {
428
+ topLevelScope.knownVariableTypes.set(name, { cppType });
429
+ }
430
+ }
431
+ // Register namespace-scope const/let types in knownVariableTypes so the
432
+ // snprintf operand resolver (and other type-resolution consumers) see them
433
+ // — otherwise a namespace `const string` used in a concat fell through to
434
+ // the %d default (namespace stress test Finding 3b). Mirrors the cross-
435
+ // module registration above; walks nested namespaces recursively.
436
+ const registerNsConstants = (ns) => {
437
+ for (const c of ns.constants) {
438
+ topLevelScope.knownVariableTypes.set(c.name, { cppType: c.cppType });
439
+ }
440
+ for (const child of ns.children ?? [])
441
+ registerNsConstants(child);
442
+ };
443
+ for (const ns of program.namespaces)
444
+ registerNsConstants(ns);
445
+ const stringVarNames = new Set();
446
+ // Pre-populate known string variable names from the program IR so that
447
+ // string-concat / template rendering can detect string identifiers even when
448
+ // the dynamic knownVariableTypes map doesn't yet hold them at render time.
449
+ const isStringCppType = (t) => !!t && (t === "std::string" || t === "const char*");
450
+ const collectStringVars = (statements) => {
451
+ for (const stmt of statements) {
452
+ switch (stmt.kind) {
453
+ case "var_decl":
454
+ if (isStringCppType(stmt.cppType))
455
+ stringVarNames.add(stmt.name);
456
+ break;
457
+ case "if":
458
+ collectStringVars(stmt.thenBranch);
459
+ if (stmt.elseBranch)
460
+ collectStringVars(stmt.elseBranch);
461
+ break;
462
+ case "for":
463
+ if (stmt.initializer && stmt.initializer.kind === "var_decl" && isStringCppType(stmt.initializer.cppType)) {
464
+ stringVarNames.add(stmt.initializer.name);
465
+ }
466
+ collectStringVars(stmt.body);
467
+ break;
468
+ case "for_of":
469
+ case "for_in":
470
+ if (stmt.variable && stmt.variable.kind === "var_decl" && isStringCppType(stmt.variable.cppType)) {
471
+ stringVarNames.add(stmt.variable.name);
472
+ }
473
+ collectStringVars(stmt.body);
474
+ break;
475
+ case "while":
476
+ case "do_while":
477
+ case "block":
478
+ case "labeled":
479
+ collectStringVars(stmt.body);
480
+ break;
481
+ case "switch":
482
+ for (const c of stmt.cases)
483
+ collectStringVars(c.body);
484
+ break;
485
+ case "try":
486
+ collectStringVars(stmt.tryBlock);
487
+ if (stmt.catchBlock)
488
+ collectStringVars(stmt.catchBlock);
489
+ if (stmt.finallyBlock)
490
+ collectStringVars(stmt.finallyBlock);
491
+ break;
492
+ default:
493
+ break;
494
+ }
495
+ }
496
+ };
497
+ for (const stmt of program.topLevelStatements) {
498
+ if (stmt.kind === "var_decl" && isStringCppType(stmt.cppType))
499
+ stringVarNames.add(stmt.name);
500
+ }
501
+ for (const fn of program.functions) {
502
+ for (const param of fn.parameters) {
503
+ if (isStringCppType(param.cppType))
504
+ stringVarNames.add(param.name);
505
+ }
506
+ collectStringVars(fn.statements);
507
+ }
508
+ for (const cls of program.classes) {
509
+ for (const field of cls.fields) {
510
+ if (isStringCppType(field.cppType))
511
+ stringVarNames.add(field.name);
512
+ }
513
+ for (const method of cls.methods) {
514
+ for (const param of method.parameters) {
515
+ if (isStringCppType(param.cppType))
516
+ stringVarNames.add(param.name);
517
+ }
518
+ collectStringVars(method.statements);
519
+ }
520
+ if (cls.constructor) {
521
+ for (const param of cls.constructor.parameters) {
522
+ if (isStringCppType(param.cppType))
523
+ stringVarNames.add(param.name);
524
+ }
525
+ collectStringVars(cls.constructor.statements);
526
+ }
527
+ }
528
+ const varAccessorNames = new Map();
529
+ // Populate the variable → accessor map BEFORE constructing the renderers so
530
+ // every emit pass (free functions, class methods, top-level code) sees the
531
+ // full mapping. A getter read (`hero.alive`) must rewrite to `hero->getAlive()`
532
+ // regardless of whether `hero` is a local, a free-function parameter, a class
533
+ // method/ctor parameter, or a top-level binding. Previously only local
534
+ // var_decls were registered (in class-emitter), which ran too late for the
535
+ // free-function pass — leaving `hero->alive` unrewritten and failing g++.
536
+ // See SUPPORT_MATRIX §4.3 (demo #4 fix).
537
+ // `classAccessorNames` is built at the outer scope (not inside a nested
538
+ // block) so it can also be passed to the expression renderer as
539
+ // `typeAccessorNames` — the type-keyed fallback used when a getter access's
540
+ // receiver isn't a registered variable (e.g. a for-of loop variable, a
541
+ // function return, or a chained member access). See demo #6 fix C.
542
+ const classAccessorNames = new Map();
543
+ for (const classDef of program.classes) {
544
+ const accessors = new Map();
545
+ for (const g of classDef.getters) {
546
+ accessors.set(g.name, accessors.has(g.name) ? "both" : "getter");
547
+ }
548
+ for (const s of classDef.setters) {
549
+ accessors.set(s.name, accessors.has(s.name) ? "both" : "setter");
550
+ }
551
+ if (accessors.size > 0) {
552
+ classAccessorNames.set(classDef.name, accessors);
553
+ }
554
+ }
555
+ // Merge cross-file (imported) class accessors so `obj.getter` and
556
+ // `Cls.staticGetter` accesses rewrite correctly when the class lives in
557
+ // another module (demo #14 Finding E).
558
+ if (options.crossModuleClassAccessors) {
559
+ for (const [className, accessors] of options.crossModuleClassAccessors) {
560
+ if (!classAccessorNames.has(className)) {
561
+ classAccessorNames.set(className, accessors);
562
+ }
563
+ }
564
+ }
565
+ const allVarDecls = [];
566
+ for (const stmt of program.topLevelStatements) {
567
+ if (stmt.kind === "var_decl")
568
+ allVarDecls.push(stmt);
569
+ }
570
+ for (const fn of mappedFunctions) {
571
+ for (const stmt of fn.statements) {
572
+ if (stmt.kind === "var_decl")
573
+ allVarDecls.push(stmt);
574
+ }
575
+ for (const param of fn.parameters) {
576
+ allVarDecls.push({ name: param.name, cppType: param.cppType });
577
+ }
578
+ }
579
+ for (const cls of program.classes) {
580
+ for (const method of cls.methods) {
581
+ for (const param of method.parameters) {
582
+ allVarDecls.push({ name: param.name, cppType: param.cppType });
583
+ }
584
+ }
585
+ if (cls.constructor) {
586
+ for (const param of cls.constructor.parameters) {
587
+ allVarDecls.push({ name: param.name, cppType: param.cppType });
588
+ }
589
+ }
590
+ }
591
+ for (const v of allVarDecls) {
592
+ const bareType = v.cppType.replace(/\*$/, "").replace(/^const\s+/, "");
593
+ const accessors = classAccessorNames.get(bareType);
594
+ if (accessors) {
595
+ varAccessorNames.set(v.name, accessors);
596
+ }
597
+ }
598
+ const exprRenderer = new ExpressionRenderer({
599
+ strategy,
600
+ boardConstants: program.boardConstants,
601
+ typeAccessorNames: classAccessorNames,
602
+ classNameMap,
603
+ enumNames,
604
+ stringEnumNames,
605
+ largeEnumNames,
606
+ knownFunctionReturnTypes,
607
+ knownVariableTypes: topLevelScope.knownVariableTypes,
608
+ globalPointerVarTypes,
609
+ namespaceNames,
610
+ snprintfCounter,
611
+ stringVarNames,
612
+ varAccessorNames,
613
+ interfaceFieldTypes,
614
+ crossModuleClassNames: classNames,
615
+ knownTopLevelObjectTypes,
616
+ diagnostics: emitDiagnostics,
617
+ });
618
+ const statementRenderer = new StatementRenderer({
619
+ strategy,
620
+ boardConstants: program.boardConstants,
621
+ classNameMap,
622
+ enumNames,
623
+ stringEnumNames,
624
+ largeEnumNames,
625
+ knownFunctionReturnTypes,
626
+ knownVariableTypes: topLevelScope.knownVariableTypes,
627
+ globalPointerVarTypes,
628
+ namespaceNames,
629
+ snprintfCounter,
630
+ stringVarNames,
631
+ varAccessorNames,
632
+ typeAccessorNames: classAccessorNames,
633
+ interfaceFieldTypes,
634
+ crossModuleClassNames: classNames,
635
+ diagnostics: emitDiagnostics,
636
+ });
637
+ const asyncFunctionOriginalNames = new Set(program.functions.filter((fn) => fn.isAsync).map((fn) => fn.originalName));
638
+ const mapFunctionNameLocal = (originalName) => statementRenderer.mapFunctionName(originalName);
639
+ const asyncFunctionMappedNames = new Set(program.functions.filter((fn) => fn.isAsync).map((fn) => mapFunctionNameLocal(fn.originalName)));
640
+ const asyncTaskClasses = [];
641
+ if (hasAsyncRuntime) {
642
+ for (const fn of program.functions) {
643
+ if (fn.isAsync) {
644
+ const task = generateAsyncTaskClass(fn.originalName, fn.statements, strategy, knownFunctionReturnTypes, (stmt, forHeader, strategy, pointerVarTypes, calleeTransformer, knownReturnTypes) => {
645
+ const contextRenderer = new StatementRenderer({
646
+ strategy,
647
+ boardConstants: program.boardConstants,
648
+ classNameMap,
649
+ enumNames,
650
+ stringEnumNames,
651
+ largeEnumNames,
652
+ knownFunctionReturnTypes: knownReturnTypes ?? knownFunctionReturnTypes,
653
+ knownVariableTypes: topLevelScope.knownVariableTypes,
654
+ globalPointerVarTypes,
655
+ namespaceNames,
656
+ snprintfCounter,
657
+ stringVarNames,
658
+ varAccessorNames,
659
+ typeAccessorNames: classAccessorNames,
660
+ pointerVarTypes,
661
+ diagnostics: emitDiagnostics,
662
+ });
663
+ // Use renderWithPrelude so snprintf buffer declarations (e.g.
664
+ // char __cuttlefish_str_N[...]; snprintf(...)) are emitted before
665
+ // the statement that references them. Without this, template-literal
666
+ // console.log inside async functions loses the buffer declaration.
667
+ const { prelude, statement } = contextRenderer.renderWithPrelude(stmt, forHeader, calleeTransformer);
668
+ if (prelude.length > 0) {
669
+ return prelude.join("\n") + "\n" + statement;
670
+ }
671
+ return statement;
672
+ });
673
+ asyncTaskClasses.push({ ...task, taskVarName: `${fn.originalName}Task` });
674
+ }
675
+ }
676
+ }
677
+ // Additional includes computed from analysis
678
+ if (strategy.needsIostream() && programAnalysis.hasConsoleCalls) {
679
+ includes.push("<iostream>");
680
+ }
681
+ const usesVectorTypes = programAnalysis.usesVectorTypes || programAnalysis.hasArrayInObjectLiteral;
682
+ if (programAnalysis.usesStdString && strategy.needsStdString()) {
683
+ includes.push("<string>");
684
+ }
685
+ if (strategy.needsStdVector() && programAnalysis.usesVectorTypes) {
686
+ includes.push("<vector>");
687
+ }
688
+ if (strategy.needsStdVector() && programAnalysis.hasArrayInObjectLiteral) {
689
+ includes.push("<vector>");
690
+ }
691
+ if (programAnalysis.usesStdFunction && strategy.needsStdFunction()) {
692
+ includes.push("<functional>");
693
+ }
694
+ if (programAnalysis.hasThrowStatements && strategy.needsStdExcept()) {
695
+ includes.push("<stdexcept>");
696
+ }
697
+ if (program.functions.some(fn => fn.typeParameterConstraints && fn.typeParameterConstraints.size > 0)) {
698
+ includes.push("<type_traits>");
699
+ }
700
+ if (programAnalysis.hasStdMathCalls) {
701
+ includes.push(strategy.mathHeader());
702
+ }
703
+ if (programAnalysis.usesStdMap) {
704
+ includes.push("<map>");
705
+ }
706
+ const needsSnprintf = strategy.useSnprintfForStrings() && (program.topLevelStatements.some((statement) => statementNeedsSnprintf(statement, strategy)) ||
707
+ program.functions.some((fn) => fn.statements.some((statement) => statementNeedsSnprintf(statement, strategy))) ||
708
+ program.classes.some((classDef) => (classDef.constructor?.statements.some((statement) => statementNeedsSnprintf(statement, strategy)) ?? false) ||
709
+ classDef.methods.some((method) => method.statements.some((statement) => statementNeedsSnprintf(statement, strategy)))));
710
+ if (needsSnprintf) {
711
+ includes.push("<stdio.h>");
712
+ includes.push("<stdlib.h>");
713
+ }
714
+ if (emittedPolyfills) {
715
+ includes.push(...emittedPolyfills.includes.map((include) => normalizeInclude(include)));
716
+ }
717
+ if (program.requiredIncludes) {
718
+ includes.push(...program.requiredIncludes);
719
+ }
720
+ // UI text bindings lower to snprintf bodies that need <stdio.h>. Pushed here
721
+ // (in buildEmitterContext) rather than in emitUIRuntime because emitPreamble
722
+ // runs before emitUIRuntime — a push there would never reach the output.
723
+ // Gated on entryHasUI so non-UI files don't pull in stdio unnecessarily.
724
+ if (entryHasUI() && !includes.includes("<stdio.h>")) {
725
+ includes.push("<stdio.h>");
726
+ }
727
+ // std::variant (from discriminated-union type aliases) needs <variant>.
728
+ // Scan type aliases AND function signatures (params/return types) — a union
729
+ // used only as a param/return may not survive tree-shaking as an alias, but
730
+ // the resolved `std::variant<...>` cppType still appears in signatures.
731
+ const variantInAliases = program.typeAliases.some((a) => (a.cppType ?? "").includes("std::variant"));
732
+ const variantInFunctions = program.functions.some((f) => (f.returnType ?? "").includes("std::variant")
733
+ || f.parameters.some((p) => (p.cppType ?? "").includes("std::variant")));
734
+ if (variantInAliases || variantInFunctions) {
735
+ includes.push("<variant>");
736
+ }
737
+ // String enums lower to const char* and use strcmp() for === comparisons.
738
+ // Header name is platform-specific: <cstring> on hosted, <string.h> on AVR.
739
+ if (stringEnumNames.size > 0) {
740
+ includes.push(strategy.cstringHeader());
741
+ }
742
+ // Placeholder defaults for fields that are computed later by other phases
743
+ const noopFixPointer = (c) => c;
744
+ // Demo #18 Finding B: collect the names of non-exported free functions that
745
+ // are CALLED from at least one class method body (method/getter/setter/
746
+ // constructor). In split mode an inline class method body lives in the
747
+ // header, so such a function must be visible in the header too (a `static`
748
+ // forward decl in the .cpp is reached only after the header is processed).
749
+ // These functions are emitted non-static with a header prototype, like
750
+ // exported functions.
751
+ //
752
+ // Demo #30 Finding A: this previously had its OWN hand-rolled IR walk that
753
+ // only inspected structured `call`/`method-call` nodes. A free function
754
+ // reached through a `raw` IR wrapper — the lowering of
755
+ // `freeFn(x).trim()` / `__tc_trim(freeFn(x))` — was invisible to it, so the
756
+ // function was emitted `static` with no header prototype and g++ reported it
757
+ // "not declared in this scope" from the inline class-method body. This is
758
+ // the SAME blind spot demo #28 Finding C fixed in the tree-shaking walk
759
+ // (`identifier-collector.ts`): two parallel walks over the same IR diverged.
760
+ // The fix is to stop duplicating the walk and reuse the canonical collector
761
+ // — which already extracts identifiers from `raw` text, recurses through
762
+ // `paren`/`lambda`/`tuple-access`/`hal-expr`, and is the single place that
763
+ // knows every IR shape. Any free function reached through ANY lowering
764
+ // (raw wrapper, parenthesized call, lambda capture, HAL op) is now visible.
765
+ const freeFunctionNames = new Set();
766
+ for (const fn of program.functions) {
767
+ if (!fn.isExported)
768
+ freeFunctionNames.add(fn.originalName);
769
+ }
770
+ const freeFunctionsCalledFromClassMethods = new Set();
771
+ for (const cls of program.classes) {
772
+ const methodBodies = [];
773
+ for (const m of cls.methods)
774
+ for (const s of m.statements)
775
+ methodBodies.push(s);
776
+ for (const g of cls.getters)
777
+ for (const s of g.statements)
778
+ methodBodies.push(s);
779
+ for (const st of cls.setters)
780
+ for (const s of st.statements)
781
+ methodBodies.push(s);
782
+ if (cls.constructor)
783
+ for (const s of cls.constructor.statements)
784
+ methodBodies.push(s);
785
+ for (const stmt of methodBodies) {
786
+ for (const id of collectStatementIdentifiers(stmt)) {
787
+ if (freeFunctionNames.has(id))
788
+ freeFunctionsCalledFromClassMethods.add(id);
789
+ }
790
+ }
791
+ }
792
+ return {
793
+ program,
794
+ options,
795
+ strategy,
796
+ programAnalysis,
797
+ enumNames,
798
+ stringEnumNames,
799
+ largeEnumNames,
800
+ namespaceNames,
801
+ symbolMap,
802
+ includes,
803
+ shimLines,
804
+ baseName,
805
+ effectiveEmitMode,
806
+ isEntryFile,
807
+ isNpmPackage,
808
+ isrPrefix,
809
+ reservedNames,
810
+ knownFunctionReturnTypes,
811
+ mappedFunctions,
812
+ freeFunctionsCalledFromClassMethods,
813
+ topLevelScope,
814
+ snprintfCounter,
815
+ stringVarNames,
816
+ varAccessorNames,
817
+ exprRenderer,
818
+ statementRenderer,
819
+ classNameMap,
820
+ boardConstants: program.boardConstants,
821
+ restParamFunctions: program.restParamFunctions,
822
+ asyncTaskClasses,
823
+ asyncFunctionOriginalNames,
824
+ asyncFunctionMappedNames,
825
+ hasAsyncRuntime,
826
+ hasPromiseRuntime,
827
+ usesTimers,
828
+ emittedPolyfills,
829
+ sourceLines: [],
830
+ headerLines: ["#pragma once", ""],
831
+ sourceMapEntries: [],
832
+ headerMapEntries: [],
833
+ cArrayVarNames: new Set(),
834
+ fnCArrayVarNames: new Map(),
835
+ globalPointerVarTypes,
836
+ promotedVarDecls: new Map(),
837
+ callbackFunctions: [],
838
+ filteredTopLevelExecutables: [],
839
+ filteredTopLevelDeclarations: [],
840
+ emittedTopLevelStatements: [],
841
+ compiletimeVarNames: new Set(),
842
+ fixPointerFieldAccess: noopFixPointer,
843
+ knownTopLevelObjectTypes,
844
+ knownTopLevelObjectFields: new Map(),
845
+ profileDiagnostics,
846
+ emitDiagnostics,
847
+ templateInterfaceNames,
848
+ interfaceNamespaceMap,
849
+ interfaceFieldTypes,
850
+ };
851
+ }