@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,758 @@
1
+ import ts from "typescript";
2
+ import { requiredIncludes, mutableArrayVars, nestedClassAliases, hoistedNestedClasses, topLevelClassNames, topLevelClasses, activeEnumNames, hoistedNestedFunctions, getContext } from "../build-ir-state.js";
3
+ import { getCurrentIrTypeScope } from "../symbol-types.js";
4
+ import { extractNodeComments, makeSourceSpan } from "../ast-node-utils.js";
5
+ import { tryResolveHALMethod } from "./hal-call-resolver.js";
6
+ import { tryResolveUICall, isSignalName, resolveUIModuleImport, recordPressBinding, uiPressBindings, resolveNodeIndex, resolveNodeTag, watchPinSpecs, recordWatchPin, recordClickHandler, clickHandlers, pushUnknownElementDiagnostic } from "./ui-call-resolver.js";
7
+ import { lowerCallbackStatements, resolveCallbackArg, getCanvasAmbientCtx, getCanvasAmbientDiagnostics, getCanvasAmbientSourceText, } from "./ui-callback-lowering.js";
8
+ import { rewriteCanvasCall } from "./canvas-lowering.js";
9
+ import { callbackContextLabel, unsupportedStatementHint } from "./callback-context-registry.js";
10
+ import { tryLowerArrayAndStringMethods } from "./array-methods.js";
11
+ import { expressionToIR } from "../expression-to-ir.js";
12
+ import { lowerStatementList } from "../statement-to-ir.js";
13
+ import { escapeCppKeyword } from "../../utils/strings.js";
14
+ import { renderExprAsText, calleeToText } from "../render-expr.js";
15
+ import { parseCppType, renderCppType, parsedIsPointer, parsedIsMap, parsedIsSet } from "../../api/shared/cpp-type-ir.js";
16
+ /**
17
+ * Resolve a `screen.<id>` or `screen.groups.<screenId>.<id>` element receiver
18
+ * (the object a method like .onClick is called on) into its tree name, element
19
+ * handle, and optional screen-id scope. Returns undefined when the expression
20
+ * is neither shape. The screenId is present only for the grouped form; callers
21
+ * pass it to resolveNodeIndex/resolveNodeTag to scope the search.
22
+ */
23
+ function resolveElementReceiver(expr) {
24
+ if (!ts.isPropertyAccessExpression(expr))
25
+ return undefined;
26
+ // Flat: screen.<id> → expr.expression is an Identifier ("screen").
27
+ if (ts.isIdentifier(expr.expression)) {
28
+ return { treeName: expr.expression.text, elemId: expr.name.text };
29
+ }
30
+ // Grouped: screen.groups.<screenId>.<id> → expr.expression is
31
+ // PropertyAccess(screen.groups, screenId), i.e. two levels deeper.
32
+ const inner = expr.expression; // screen.groups.<screenId>
33
+ if (ts.isPropertyAccessExpression(inner) &&
34
+ ts.isPropertyAccessExpression(inner.expression) &&
35
+ ts.isIdentifier(inner.expression.expression) &&
36
+ inner.expression.expression.text === "screen" &&
37
+ inner.expression.name.text === "groups") {
38
+ return { treeName: "screen", elemId: expr.name.text, screenId: inner.name.text };
39
+ }
40
+ return undefined;
41
+ }
42
+ /**
43
+ * When a Map/Set key is an enum-typed expression and the container's key type
44
+ * is an integral type, the lowered `map[key]` / `map.count(key)` / `map.at(key)`
45
+ * would pass the enum operand directly. A C++ `enum class` does not implicitly
46
+ * convert to the map's integral key type, so g++ rejects it ("no match for
47
+ * 'operator[]' ... 'K' to 'const int&'"). Wrap the key in
48
+ * `static_cast<KeyType>(...)` so it matches.
49
+ *
50
+ * This handles BOTH shapes of enum-typed key operand:
51
+ * - an enum member access (`Color.Red`, `Op.Inc`) — detected via
52
+ * `activeEnumNames` on the object identifier; and
53
+ * - a bare identifier whose declared type is an enum (`k` where `k: K`) —
54
+ * resolved through the in-scope IR type map (the gap demo #28 Finding E
55
+ * surfaced: previously only enum-member access was cast, so a bare
56
+ * enum-typed key variable on `.set`/`.has`/`.get`/`.delete` compiled to an
57
+ * uncast `map[k]`/`map.count(k)`/`map.at(k)` and failed at g++ time).
58
+ */
59
+ function castEnumKeyIfNeeded(keyText, keyNode, receiverType) {
60
+ // Only relevant for std::map/std::set with an integral key type.
61
+ if (!parsedIsMap(receiverType) && !parsedIsSet(receiverType))
62
+ return keyText;
63
+ const containerIr = parseCppType(receiverType);
64
+ const keyType = containerIr.kind === "map" ? renderCppType(containerIr.key) : "";
65
+ const isIntegral = /^(int|int8_t|int16_t|int32_t|int64_t|uint8_t|uint16_t|uint32_t|uint64_t|size_t|long|short|unsigned|char)$/.test(keyType);
66
+ if (!isIntegral)
67
+ return keyText;
68
+ // Detect an enum-typed key operand.
69
+ let isEnum = false;
70
+ if (ts.isPropertyAccessExpression(keyNode) && ts.isIdentifier(keyNode.expression)) {
71
+ // Enum member access: `Color.Red`.
72
+ isEnum = activeEnumNames.has(keyNode.expression.text);
73
+ }
74
+ else if (ts.isIdentifier(keyNode)) {
75
+ // Bare identifier: resolve its declared type through the IR type scope.
76
+ // If it is an enum name, the operand is enum-typed and needs the cast.
77
+ const varType = getCurrentIrTypeScope()?.locals.get(keyNode.text) ?? getCurrentIrTypeScope()?.globals.get(keyNode.text);
78
+ if (varType && activeEnumNames.has(varType)) {
79
+ isEnum = true;
80
+ }
81
+ }
82
+ if (!isEnum)
83
+ return keyText;
84
+ return `static_cast<${keyType}>(${keyText})`;
85
+ }
86
+ /** Lower a void UI event callback (onClick/onHold/onRelease/arity-1 onChange)
87
+ * to bodyStatements + optional named-ref metadata. */
88
+ function lowerVoidEventCallback(cbArg, fileName, sourceText, diagnostics, label) {
89
+ const resolved = resolveCallbackArg(cbArg, cbArg?.getSourceFile(), diagnostics, label);
90
+ if (!resolved)
91
+ return {};
92
+ const sourceSpan = cbArg ? makeSourceSpan(cbArg, fileName, sourceText) : undefined;
93
+ if (resolved.kind === "inline") {
94
+ return {
95
+ bodyStatements: lowerCallbackStatements(resolved.fn, fileName, sourceText, diagnostics, "ui-event-callback"),
96
+ sourceSpan,
97
+ };
98
+ }
99
+ return { isNamedRef: true, namedFn: resolved.name, sourceSpan };
100
+ }
101
+ /** Lower an optional callback for watchPin-style wrappers that have a prelude
102
+ * (onToggle / onChange(pin,count)). Named refs become a call after prelude. */
103
+ function lowerPreludeCallback(cbArg, fileName, sourceText, diagnostics, label, prelude) {
104
+ const resolved = resolveCallbackArg(cbArg, cbArg?.getSourceFile(), diagnostics, label);
105
+ const sourceSpan = cbArg ? makeSourceSpan(cbArg, fileName, sourceText) : undefined;
106
+ if (!resolved)
107
+ return { bodyStatements: prelude, sourceSpan };
108
+ if (resolved.kind === "inline") {
109
+ return {
110
+ bodyStatements: [
111
+ ...prelude,
112
+ ...lowerCallbackStatements(resolved.fn, fileName, sourceText, diagnostics, "ui-event-callback"),
113
+ ],
114
+ sourceSpan,
115
+ };
116
+ }
117
+ return {
118
+ bodyStatements: [
119
+ ...prelude,
120
+ {
121
+ kind: "call",
122
+ sourceSpan: sourceSpan ?? makeSourceSpan(cbArg, fileName, sourceText),
123
+ callee: resolved.name,
124
+ args: [],
125
+ },
126
+ ],
127
+ sourceSpan,
128
+ };
129
+ }
130
+ export function callToStatement(statementNode, call, fileName, sourceText, diagnostics, pointerVars = new Map()) {
131
+ const comments = extractNodeComments(statementNode, sourceText);
132
+ // ── setInterval/setTimeout arrow-callback hoisting (BEFORE HAL) ────────
133
+ // The timer helpers are runtime utilities, never HAL methods, but the HAL
134
+ // resolver treats bare-identifier globals as pseudo-HAL calls and claims
135
+ // them — baking the arrow arg into a lambda placeholder before our hoist
136
+ // can run. Intercept timer calls with arrow callbacks FIRST, hoist the
137
+ // arrow to a named function, and short-circuit before HAL sees it.
138
+ if (ts.isIdentifier(call.expression) && TIMER_CALLEES.has(call.expression.text)) {
139
+ const hoisted = hoistTimerArrowArg(call, fileName, sourceText, diagnostics, pointerVars, comments);
140
+ if (hoisted)
141
+ return hoisted;
142
+ }
143
+ // ── Ambient canvas ctx: rewrite ctx.method(...) while drawCanvas lowers ──
144
+ {
145
+ const canvasCtx = getCanvasAmbientCtx();
146
+ if (canvasCtx) {
147
+ const ambientDiags = getCanvasAmbientDiagnostics() ?? diagnostics;
148
+ const ambientSrc = getCanvasAmbientSourceText() ?? sourceText;
149
+ if (ts.isPropertyAccessExpression(call.expression) &&
150
+ ts.isIdentifier(call.expression.expression) &&
151
+ call.expression.expression.text === canvasCtx) {
152
+ const rewritten = rewriteCanvasCall(call, canvasCtx, ambientDiags, ambientSrc);
153
+ return {
154
+ kind: "call",
155
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
156
+ leadingComments: comments.leadingComments,
157
+ trailingComments: comments.trailingComments,
158
+ callee: "__EMIT__",
159
+ args: [{ kind: "string", value: (rewritten ?? "").replace(/;$/, "") }],
160
+ };
161
+ }
162
+ ambientDiags.push({
163
+ severity: "error",
164
+ code: "ui-callback-unsupported-statement",
165
+ message: `Unsupported statement in ${callbackContextLabel("ui-draw-canvas")}: only ctx.* drawing calls are allowed here.`,
166
+ hint: unsupportedStatementHint("ui-draw-canvas"),
167
+ source: ambientSrc.slice(Math.max(0, call.getStart() - 40), call.getEnd() + 10).trim(),
168
+ });
169
+ return {
170
+ kind: "block",
171
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
172
+ leadingComments: comments.leadingComments,
173
+ trailingComments: comments.trailingComments,
174
+ body: [],
175
+ };
176
+ }
177
+ }
178
+ // ── signal.set(value) → assignment ─────────────────────────────────────
179
+ // A signal lowers to a plain device variable; its .set() method is just an
180
+ // assignment. Intercept before HAL so it doesn't try to resolve .set as a
181
+ // HAL method on the variable.
182
+ if (ts.isPropertyAccessExpression(call.expression) &&
183
+ call.expression.name.text === "set" &&
184
+ ts.isIdentifier(call.expression.expression) &&
185
+ isSignalName(call.expression.expression.text)) {
186
+ const valueIR = call.arguments[0] ? expressionToIR(call.arguments[0], sourceText, diagnostics, pointerVars) : { kind: "number", value: 0 };
187
+ return {
188
+ kind: "assign",
189
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
190
+ leadingComments: comments.leadingComments,
191
+ trailingComments: comments.trailingComments,
192
+ target: call.expression.expression.text,
193
+ operator: "=",
194
+ value: valueIR,
195
+ };
196
+ }
197
+ // ── screen.btn.onPress(pin) / onRelease(pin) ───────────────────────────
198
+ // Lowers to a press-binding record: the emit layer emits a handler function
199
+ // (ui_on_press(nodeIndex)) and an attachInterrupt in setup(). The call shape
200
+ // is <tree>.<id>.onPress(<pin>) — a chained property access on an imported
201
+ // UI tree element.
202
+ if (ts.isPropertyAccessExpression(call.expression) &&
203
+ (call.expression.name.text === "onPress" || call.expression.name.text === "onRelease") &&
204
+ ts.isPropertyAccessExpression(call.expression.expression) &&
205
+ ts.isIdentifier(call.expression.expression.expression)) {
206
+ const treeName = call.expression.expression.expression.text;
207
+ const elemId = call.expression.expression.name.text;
208
+ const edge = call.expression.name.text === "onPress" ? "press" : "release";
209
+ const pinArg = call.arguments[0];
210
+ const pinText = pinArg ? (ts.isIdentifier(pinArg) ? pinArg.text : String(pinArg.getText())) : "0";
211
+ const htmlPath = resolveUIModuleImport(treeName);
212
+ const nodeIndex = htmlPath ? resolveNodeIndex(htmlPath, elemId) : 0;
213
+ if (nodeIndex < 0) {
214
+ pushUnknownElementDiagnostic(diagnostics, `screen.${elemId}.${call.expression.name.text}`, elemId, treeName);
215
+ return { kind: "block", sourceSpan: makeSourceSpan(call, fileName, sourceText), leadingComments: comments.leadingComments, trailingComments: comments.trailingComments, body: [] };
216
+ }
217
+ const handlerName = `__ui_${elemId}_${edge}_${uiPressBindings().length}`;
218
+ recordPressBinding({ nodeIndex, pin: pinText, edge, handlerName });
219
+ return {
220
+ kind: "block",
221
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
222
+ leadingComments: comments.leadingComments,
223
+ trailingComments: comments.trailingComments,
224
+ body: [],
225
+ };
226
+ }
227
+ // ── screen.led.onToggle(pin, callback) — checkbox toggle ──────────────
228
+ // Flips the node's checked state on falling edge and optionally calls
229
+ // the user's callback (for signal writes). Uses the pin-watching system.
230
+ if (ts.isPropertyAccessExpression(call.expression) &&
231
+ call.expression.name.text === "onToggle" &&
232
+ ts.isPropertyAccessExpression(call.expression.expression) &&
233
+ ts.isIdentifier(call.expression.expression.expression)) {
234
+ const treeName = call.expression.expression.expression.text;
235
+ const elemId = call.expression.expression.name.text;
236
+ const pinArg = call.arguments[0];
237
+ const cbArg = call.arguments[1];
238
+ const pin = pinArg ? (ts.isNumericLiteral(pinArg) ? pinArg.text : pinArg.getText()) : "0";
239
+ const htmlPath = resolveUIModuleImport(treeName);
240
+ const nodeIndex = htmlPath ? resolveNodeIndex(htmlPath, elemId) : 0;
241
+ if (nodeIndex < 0) {
242
+ pushUnknownElementDiagnostic(diagnostics, `screen.${elemId}.${call.expression.name.text}`, elemId, treeName);
243
+ return { kind: "block", sourceSpan: makeSourceSpan(call, fileName, sourceText), leadingComments: comments.leadingComments, trailingComments: comments.trailingComments, body: [] };
244
+ }
245
+ const fnName = `__ui_${elemId}_toggle_${watchPinSpecs().length}`;
246
+ const prelude = [
247
+ {
248
+ kind: "call",
249
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
250
+ callee: "__EMIT__",
251
+ args: [{
252
+ kind: "string",
253
+ value: `__ui_nodes[${nodeIndex}].value = !__ui_nodes[${nodeIndex}].value; ui_mark_dirty(${nodeIndex})`,
254
+ }],
255
+ },
256
+ ];
257
+ const { bodyStatements, sourceSpan } = lowerPreludeCallback(cbArg, fileName, sourceText, diagnostics, `screen.${elemId}.onToggle`, prelude);
258
+ recordWatchPin({ pin: String(pin), fnName, callbackBody: "", bodyStatements, sourceSpan });
259
+ return {
260
+ kind: "block",
261
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
262
+ leadingComments: comments.leadingComments,
263
+ trailingComments: comments.trailingComments,
264
+ body: [],
265
+ };
266
+ }
267
+ // ── screen.input.onChange(callback) — input text committed via keyboard ─
268
+ // Distinct from the GPIO onChange below: this variant takes a single callback
269
+ // arg (no pin/count) and fires after ui_kb_close commits the typed text.
270
+ // Also handles <range> onChange — routed to "rangechange" and fired from the
271
+ // drag loop on every value change during a slider drag.
272
+ if (ts.isPropertyAccessExpression(call.expression) &&
273
+ call.expression.name.text === "onChange" &&
274
+ ts.isPropertyAccessExpression(call.expression.expression) &&
275
+ ts.isIdentifier(call.expression.expression.expression) &&
276
+ call.arguments.length === 1) {
277
+ const treeName = call.expression.expression.expression.text;
278
+ const elemId = call.expression.expression.name.text;
279
+ const cbArg = call.arguments[0];
280
+ const htmlPath = resolveUIModuleImport(treeName);
281
+ const nodeIndex = htmlPath ? resolveNodeIndex(htmlPath, elemId) : 0;
282
+ if (nodeIndex < 0) {
283
+ pushUnknownElementDiagnostic(diagnostics, `screen.${elemId}.${call.expression.name.text}`, elemId, treeName);
284
+ return { kind: "block", sourceSpan: makeSourceSpan(call, fileName, sourceText), leadingComments: comments.leadingComments, trailingComments: comments.trailingComments, body: [] };
285
+ }
286
+ const nodeTag = htmlPath ? resolveNodeTag(htmlPath, elemId) : "";
287
+ const kind = nodeTag === "range" ? "rangechange" : "change";
288
+ const lowered = lowerVoidEventCallback(cbArg, fileName, sourceText, diagnostics, `screen.${elemId}.onChange`);
289
+ const fnName = lowered.isNamedRef && lowered.namedFn
290
+ ? lowered.namedFn
291
+ : `__ui_${elemId}_${kind}_${clickHandlers().length}`;
292
+ recordClickHandler({
293
+ nodeIndex,
294
+ kind,
295
+ fnName,
296
+ callbackBody: "",
297
+ bodyStatements: lowered.bodyStatements,
298
+ isNamedRef: lowered.isNamedRef,
299
+ sourceSpan: lowered.sourceSpan,
300
+ });
301
+ return {
302
+ kind: "block",
303
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
304
+ leadingComments: comments.leadingComments,
305
+ trailingComments: comments.trailingComments,
306
+ body: [],
307
+ };
308
+ }
309
+ // ── screen.modeSelect.onChange(pin, optionCount, callback?) ───────────
310
+ // Cycles .value through 0..optionCount-1 on each falling edge.
311
+ if (ts.isPropertyAccessExpression(call.expression) &&
312
+ call.expression.name.text === "onChange" &&
313
+ ts.isPropertyAccessExpression(call.expression.expression) &&
314
+ ts.isIdentifier(call.expression.expression.expression)) {
315
+ const treeName = call.expression.expression.expression.text;
316
+ const elemId = call.expression.expression.name.text;
317
+ const pinArg = call.arguments[0];
318
+ const countArg = call.arguments[1];
319
+ const cbArg = call.arguments[2];
320
+ const pin = pinArg ? (ts.isNumericLiteral(pinArg) ? pinArg.text : pinArg.getText()) : "0";
321
+ const optionCount = countArg ? (ts.isNumericLiteral(countArg) ? countArg.text : "2") : "2";
322
+ const htmlPath = resolveUIModuleImport(treeName);
323
+ const nodeIndex = htmlPath ? resolveNodeIndex(htmlPath, elemId) : 0;
324
+ if (nodeIndex < 0) {
325
+ pushUnknownElementDiagnostic(diagnostics, `screen.${elemId}.${call.expression.name.text}`, elemId, treeName);
326
+ return { kind: "block", sourceSpan: makeSourceSpan(call, fileName, sourceText), leadingComments: comments.leadingComments, trailingComments: comments.trailingComments, body: [] };
327
+ }
328
+ const fnName = `__ui_${elemId}_change_${watchPinSpecs().length}`;
329
+ const prelude = [
330
+ {
331
+ kind: "call",
332
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
333
+ callee: "__EMIT__",
334
+ args: [{
335
+ kind: "string",
336
+ value: `__ui_nodes[${nodeIndex}].value = (__ui_nodes[${nodeIndex}].value + 1) % ${optionCount}; ui_mark_dirty(${nodeIndex})`,
337
+ }],
338
+ },
339
+ ];
340
+ const { bodyStatements, sourceSpan } = lowerPreludeCallback(cbArg, fileName, sourceText, diagnostics, `screen.${elemId}.onChange`, prelude);
341
+ recordWatchPin({ pin: String(pin), fnName, callbackBody: "", bodyStatements, sourceSpan });
342
+ return {
343
+ kind: "block",
344
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
345
+ leadingComments: comments.leadingComments,
346
+ trailingComments: comments.trailingComments,
347
+ body: [],
348
+ };
349
+ }
350
+ // ── screen.element.onClick(callback?) — touch click handler ──────────
351
+ // Records a click handler for touch hit-testing. No pin needed — the
352
+ // touch poll loop calls ui_handle_touch which hit-tests and dispatches.
353
+ // Supports both flat (screen.<id>) and grouped (screen.groups.<sid>.<id>).
354
+ if (ts.isPropertyAccessExpression(call.expression) &&
355
+ call.expression.name.text === "onClick") {
356
+ const recv = resolveElementReceiver(call.expression.expression);
357
+ if (recv) {
358
+ const treeName = recv.treeName;
359
+ const elemId = recv.elemId;
360
+ const cbArg = call.arguments[0];
361
+ const htmlPath = resolveUIModuleImport(treeName);
362
+ const nodeIndex = htmlPath ? resolveNodeIndex(htmlPath, elemId, recv.screenId) : 0;
363
+ if (nodeIndex < 0) {
364
+ pushUnknownElementDiagnostic(diagnostics, `screen.${elemId}.${call.expression.name.text}`, elemId, treeName);
365
+ return { kind: "block", sourceSpan: makeSourceSpan(call, fileName, sourceText), leadingComments: comments.leadingComments, trailingComments: comments.trailingComments, body: [] };
366
+ }
367
+ const lowered = lowerVoidEventCallback(cbArg, fileName, sourceText, diagnostics, `screen.${elemId}.onClick`);
368
+ const fnName = lowered.isNamedRef && lowered.namedFn
369
+ ? lowered.namedFn
370
+ : `__ui_${elemId}_click_${clickHandlers().length}`;
371
+ recordClickHandler({
372
+ nodeIndex,
373
+ kind: "click",
374
+ fnName,
375
+ callbackBody: "",
376
+ bodyStatements: lowered.bodyStatements,
377
+ isNamedRef: lowered.isNamedRef,
378
+ sourceSpan: lowered.sourceSpan,
379
+ });
380
+ return {
381
+ kind: "block",
382
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
383
+ leadingComments: comments.leadingComments,
384
+ trailingComments: comments.trailingComments,
385
+ body: [],
386
+ };
387
+ } // end if (recv)
388
+ }
389
+ // ── screen.element.onHold/onRelease — touch long-press + release handlers ─
390
+ if (ts.isPropertyAccessExpression(call.expression) &&
391
+ (call.expression.name.text === "onHold" || call.expression.name.text === "onRelease") &&
392
+ ts.isPropertyAccessExpression(call.expression.expression) &&
393
+ ts.isIdentifier(call.expression.expression.expression)) {
394
+ const kind = call.expression.name.text === "onHold" ? "hold" : "release";
395
+ const treeName = call.expression.expression.expression.text;
396
+ const elemId = call.expression.expression.name.text;
397
+ const cbArg = call.arguments[0];
398
+ const htmlPath = resolveUIModuleImport(treeName);
399
+ const nodeIndex = htmlPath ? resolveNodeIndex(htmlPath, elemId) : 0;
400
+ if (nodeIndex < 0) {
401
+ pushUnknownElementDiagnostic(diagnostics, `screen.${elemId}.${call.expression.name.text}`, elemId, treeName);
402
+ return { kind: "block", sourceSpan: makeSourceSpan(call, fileName, sourceText), leadingComments: comments.leadingComments, trailingComments: comments.trailingComments, body: [] };
403
+ }
404
+ const lowered = lowerVoidEventCallback(cbArg, fileName, sourceText, diagnostics, `screen.${elemId}.${call.expression.name.text}`);
405
+ const fnName = lowered.isNamedRef && lowered.namedFn
406
+ ? lowered.namedFn
407
+ : `__ui_${elemId}_${kind}_${clickHandlers().length}`;
408
+ recordClickHandler({
409
+ nodeIndex,
410
+ kind,
411
+ fnName,
412
+ callbackBody: "",
413
+ bodyStatements: lowered.bodyStatements,
414
+ isNamedRef: lowered.isNamedRef,
415
+ sourceSpan: lowered.sourceSpan,
416
+ });
417
+ return {
418
+ kind: "block",
419
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
420
+ leadingComments: comments.leadingComments,
421
+ trailingComments: comments.trailingComments,
422
+ body: [],
423
+ };
424
+ }
425
+ // ── emit() / rawCpp() — compile-time C++ injection ─────────────────────
426
+ // Must be BEFORE the HAL resolver — emit/rawCpp are exported from @typecad/hal
427
+ // but are NOT HAL class methods. The HAL resolver would create a pseudo-instance
428
+ // and fail to find a method body, leaving the call unresolved.
429
+ if (ts.isIdentifier(call.expression) && (call.expression.text === "emit" || call.expression.text === "rawCpp")) {
430
+ return {
431
+ kind: "call",
432
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
433
+ leadingComments: comments.leadingComments,
434
+ trailingComments: comments.trailingComments,
435
+ callee: "__EMIT__",
436
+ args: call.arguments.map(a => expressionToIR(a, sourceText, diagnostics, pointerVars)),
437
+ };
438
+ }
439
+ // ---- HAL method resolver (highest priority) ---
440
+ const halResolved = tryResolveHALMethod(call, fileName, sourceText, diagnostics, pointerVars);
441
+ if (halResolved)
442
+ return halResolved;
443
+ // ---- ui.mount / ui.signal / ui.bind — UI authoring calls ---
444
+ // Handles statement-position ui.* calls. const X = ui.signal(...) (a
445
+ // VariableDeclaration, not an ExpressionStatement) is handled in the
446
+ // variable-declaration path; here we see bare ui.signal(...) statements
447
+ // and synthesize a signal name.
448
+ const uiResolved = tryResolveUICall(call, fileName, sourceText, diagnostics);
449
+ if (uiResolved)
450
+ return uiResolved;
451
+ // Handle super() calls in constructors - transform to super_call IR for class emitter
452
+ if (call.expression.kind === ts.SyntaxKind.SuperKeyword) {
453
+ return {
454
+ kind: "super_call",
455
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
456
+ leadingComments: comments.leadingComments,
457
+ trailingComments: comments.trailingComments,
458
+ args: call.arguments.map(a => expressionToIR(a, sourceText, diagnostics, pointerVars)),
459
+ };
460
+ }
461
+ // ── include() — compile-time C++ header registration ─────────────────────
462
+ if (ts.isIdentifier(call.expression) && call.expression.text === "include") {
463
+ const firstArg = call.arguments[0];
464
+ if (firstArg && ts.isStringLiteral(firstArg)) {
465
+ requiredIncludes.add(firstArg.text);
466
+ }
467
+ // Emit nothing in C++ — return empty block
468
+ return {
469
+ kind: "block",
470
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
471
+ leadingComments: comments.leadingComments,
472
+ trailingComments: comments.trailingComments,
473
+ body: [],
474
+ };
475
+ }
476
+ // ── Structural vector/array method lowering (shared with expressionToIR) ─
477
+ // Lower .push/.pop/.map/.filter/.splice/... on a std::vector receiver to the
478
+ // __tc_* helper form (or push_back) BEFORE the generic call handling below.
479
+ // This mirrors expressionToIR's call to tryLowerArrayAndStringMethods, so the
480
+ // statement form (`out.push(x);`) and the expression form (`x = arr.pop()`)
481
+ // share one structural lowering. Previously only the StaticArray case was
482
+ // handled here (mutableArrayVars branch below); the std::vector case was left
483
+ // as a raw `out.push` callee and patched to `push_back` by a regex in the
484
+ // native strategy's normalizeRawExpression. Demo #22 / Tier-2 cleanup.
485
+ const lowered = tryLowerArrayAndStringMethods(call, sourceText, diagnostics, pointerVars);
486
+ if (lowered) {
487
+ if (lowered.kind === "method-call") {
488
+ // Callback-arg methods (.map/.filter/.sort(fn)/...): preserve the
489
+ // structured method-call node as a `call` statement so (a) the emit-time
490
+ // callback hoister finds the callback in `args` and names it, and (b)
491
+ // program-analysis detects the __tc_* helper usage from `callee`.
492
+ return {
493
+ kind: "call",
494
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
495
+ leadingComments: comments.leadingComments,
496
+ trailingComments: comments.trailingComments,
497
+ callee: lowered.callee,
498
+ args: lowered.args,
499
+ };
500
+ }
501
+ // Value-arg methods (.push/.pop/.fill/.concat/...): the lowering produced a
502
+ // fully-formed C++ expression (e.g. `out.push_back(x)` or `__tc_pop(arr)`);
503
+ // emit it as a raw statement. The __tc_* helper usage is detected by
504
+ // program-analysis via the `includes(name)` check on the call callee.
505
+ const loweredText = renderExprAsText(lowered);
506
+ return {
507
+ kind: "call",
508
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
509
+ leadingComments: comments.leadingComments,
510
+ trailingComments: comments.trailingComments,
511
+ callee: "__RAW_STMT__" + loweredText,
512
+ args: [],
513
+ };
514
+ }
515
+ // ── Array method translation: push → push_back, pop → pop_back ──────────
516
+ if (ts.isPropertyAccessExpression(call.expression)) {
517
+ const methodName = call.expression.name.text;
518
+ const objExpr = call.expression.expression;
519
+ if (ts.isIdentifier(objExpr) && mutableArrayVars.has(objExpr.text)) {
520
+ if (methodName === "push") {
521
+ return {
522
+ kind: "call",
523
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
524
+ leadingComments: comments.leadingComments,
525
+ trailingComments: comments.trailingComments,
526
+ callee: `${objExpr.text}.push`,
527
+ args: call.arguments.map(a => expressionToIR(a, sourceText, diagnostics, pointerVars)),
528
+ };
529
+ }
530
+ if (methodName === "pop") {
531
+ return {
532
+ kind: "call",
533
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
534
+ leadingComments: comments.leadingComments,
535
+ trailingComments: comments.trailingComments,
536
+ callee: `${objExpr.text}.pop`,
537
+ args: [],
538
+ };
539
+ }
540
+ }
541
+ }
542
+ // ── Map/Set method lowering: .set()/.get()/.has()/.delete()/.add() ──────
543
+ if (ts.isPropertyAccessExpression(call.expression)) {
544
+ const mapMethodName = call.expression.name.text;
545
+ if ((mapMethodName === "set" || mapMethodName === "get" || mapMethodName === "has" || mapMethodName === "delete" || mapMethodName === "add") && call.arguments.length >= 1) {
546
+ const mapReceiver = call.expression.expression;
547
+ let receiverType;
548
+ if (ts.isIdentifier(mapReceiver)) {
549
+ receiverType = getCurrentIrTypeScope()?.locals.get(mapReceiver.text) ?? getCurrentIrTypeScope()?.globals.get(mapReceiver.text);
550
+ }
551
+ else if (ts.isPropertyAccessExpression(mapReceiver) && mapReceiver.expression.kind === ts.SyntaxKind.ThisKeyword) {
552
+ receiverType = getCurrentIrTypeScope()?.locals.get(`this->${mapReceiver.name.text}`);
553
+ }
554
+ if (receiverType && (parsedIsMap(receiverType) || parsedIsSet(receiverType))) {
555
+ const recIR = expressionToIR(mapReceiver, sourceText, diagnostics, pointerVars);
556
+ const recText = renderExprAsText(recIR);
557
+ const arg0IR = expressionToIR(call.arguments[0], sourceText, diagnostics, pointerVars);
558
+ const arg0Text = renderExprAsText(arg0IR);
559
+ if (parsedIsMap(receiverType) && mapMethodName === "set" && call.arguments.length >= 2) {
560
+ const arg1IR = expressionToIR(call.arguments[1], sourceText, diagnostics, pointerVars);
561
+ const arg1Text = renderExprAsText(arg1IR);
562
+ const castedKey = castEnumKeyIfNeeded(arg0Text, call.arguments[0], receiverType);
563
+ return {
564
+ kind: "assign",
565
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
566
+ leadingComments: comments.leadingComments,
567
+ trailingComments: comments.trailingComments,
568
+ target: `${recText}[${castedKey}]`,
569
+ operator: "=",
570
+ value: { kind: "raw", value: arg1Text },
571
+ };
572
+ }
573
+ if (parsedIsMap(receiverType) && mapMethodName === "get") {
574
+ const castedKey = castEnumKeyIfNeeded(arg0Text, call.arguments[0], receiverType);
575
+ return {
576
+ kind: "call",
577
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
578
+ leadingComments: comments.leadingComments,
579
+ trailingComments: comments.trailingComments,
580
+ callee: `${recText}.at`,
581
+ args: [{ kind: "raw", value: castedKey }],
582
+ };
583
+ }
584
+ if ((parsedIsMap(receiverType) || parsedIsSet(receiverType)) && mapMethodName === "has") {
585
+ const castedKey = castEnumKeyIfNeeded(arg0Text, call.arguments[0], receiverType);
586
+ return {
587
+ kind: "call",
588
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
589
+ leadingComments: comments.leadingComments,
590
+ trailingComments: comments.trailingComments,
591
+ callee: `${recText}.count`,
592
+ args: [{ kind: "raw", value: castedKey }],
593
+ };
594
+ }
595
+ if ((parsedIsMap(receiverType) || parsedIsSet(receiverType)) && mapMethodName === "delete") {
596
+ const castedKey = castEnumKeyIfNeeded(arg0Text, call.arguments[0], receiverType);
597
+ return {
598
+ kind: "call",
599
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
600
+ leadingComments: comments.leadingComments,
601
+ trailingComments: comments.trailingComments,
602
+ callee: `${recText}.erase`,
603
+ args: [{ kind: "raw", value: castedKey }],
604
+ };
605
+ }
606
+ if (parsedIsSet(receiverType) && mapMethodName === "add") {
607
+ return {
608
+ kind: "call",
609
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
610
+ leadingComments: comments.leadingComments,
611
+ trailingComments: comments.trailingComments,
612
+ callee: `${recText}.insert`,
613
+ args: [{ kind: "raw", value: arg0Text }],
614
+ };
615
+ }
616
+ }
617
+ }
618
+ }
619
+ // Format callee, using -> for pointer variables
620
+ let calleeText;
621
+ if (ts.isPropertyAccessExpression(call.expression)) {
622
+ const objExpr = call.expression.expression;
623
+ const methodName = escapeCppKeyword(call.expression.name.text);
624
+ if (ts.isIdentifier(objExpr) && pointerVars.has(objExpr.text)) {
625
+ calleeText = `${objExpr.text}->${methodName}`;
626
+ }
627
+ else if (ts.isCallExpression(objExpr) && ts.isPropertyAccessExpression(objExpr.expression)) {
628
+ // Chained method call: obj.method1().method2()
629
+ const innerReceiver = objExpr.expression.expression;
630
+ const innerMethodName = objExpr.expression.name.text;
631
+ const innerCallText = renderExprAsText(expressionToIR(objExpr, sourceText, diagnostics, pointerVars));
632
+ let accessor = ".";
633
+ if (ts.isIdentifier(innerReceiver) && pointerVars.has(innerReceiver.text)) {
634
+ let className = pointerVars.get(innerReceiver.text);
635
+ if (className)
636
+ className = nestedClassAliases.get(className) ?? className;
637
+ const cls = className ? hoistedNestedClasses.find(c => c.name === className) : undefined;
638
+ const method = cls?.methods.find(m => m.name === innerMethodName);
639
+ if (method && parsedIsPointer(method.returnType)) {
640
+ accessor = "->";
641
+ }
642
+ }
643
+ else if (ts.isIdentifier(innerReceiver) && topLevelClassNames.has(innerReceiver.text)) {
644
+ // Static method call on a top-level or cross-module class returning an instance
645
+ const cls = topLevelClasses.get(innerReceiver.text);
646
+ if (!cls) {
647
+ // Cross-module class: factory methods typically return class instances (pointers)
648
+ accessor = "->";
649
+ }
650
+ else {
651
+ const chainMethod = cls.methods.find(m => m.name === innerMethodName);
652
+ if (chainMethod && (parsedIsPointer(chainMethod.returnType) || chainMethod.returnType === innerReceiver.text)) {
653
+ accessor = "->";
654
+ }
655
+ }
656
+ }
657
+ calleeText = `${innerCallText}${accessor}${methodName}`;
658
+ }
659
+ else {
660
+ // Use expressionToIR for pointer-aware callee construction.
661
+ // expressionToIR's call handler correctly resolves -> for pointer variables
662
+ // and chained property access (e.g. s->player->printStats), avoiding
663
+ // the dot-only calleeToText fallback.
664
+ const callIR = expressionToIR(call, sourceText, diagnostics, pointerVars);
665
+ if (callIR.kind === "method-call" && typeof callIR.callee === "string") {
666
+ return {
667
+ kind: "call",
668
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
669
+ leadingComments: comments.leadingComments,
670
+ trailingComments: comments.trailingComments,
671
+ callee: callIR.callee,
672
+ args: callIR.args,
673
+ };
674
+ }
675
+ calleeText = calleeToText(call.expression);
676
+ }
677
+ }
678
+ else {
679
+ calleeText = calleeToText(call.expression);
680
+ }
681
+ // Timer calls with arrow callbacks are intercepted before HAL (above);
682
+ // named-function callbacks fall through here unchanged.
683
+ return {
684
+ kind: "call",
685
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
686
+ leadingComments: comments.leadingComments,
687
+ trailingComments: comments.trailingComments,
688
+ callee: calleeText,
689
+ args: call.arguments.map((arg) => expressionToIR(arg, sourceText, diagnostics, pointerVars)),
690
+ };
691
+ }
692
+ // ── setInterval/setTimeout arrow-callback hoisting ──────────────────────────
693
+ const TIMER_CALLEES = new Set(["setInterval", "setTimeout", "__tc_setInterval", "__tc_setTimeout"]);
694
+ let timerCallbackCounter = 0;
695
+ /**
696
+ * Hoist an inline arrow/function-expression callback argument of a
697
+ * setInterval/setTimeout call to a named free function, and return the
698
+ * rewritten call with the function name replacing the arrow.
699
+ *
700
+ * Returns null if arg[0] isn't an inline arrow/function (named-function
701
+ * callbacks pass through to the generic path unchanged).
702
+ *
703
+ * Called from the generic fallthrough at the end of callToStatement — NOT
704
+ * as an early return — because top-level timer calls reach the fallthrough
705
+ * path, not the early-interception points.
706
+ */
707
+ function hoistTimerArrowArg(call, fileName, sourceText, diagnostics, pointerVars, comments) {
708
+ const callbackArg = call.arguments[0];
709
+ if (!callbackArg || !(ts.isArrowFunction(callbackArg) || ts.isFunctionExpression(callbackArg))) {
710
+ return null;
711
+ }
712
+ const fnName = `__tc_timer_cb_${timerCallbackCounter++}`;
713
+ const scope = getCurrentIrTypeScope();
714
+ const localVariableTypes = new Map();
715
+ if (scope) {
716
+ for (const [k, v] of scope.globals)
717
+ localVariableTypes.set(k, v);
718
+ }
719
+ const functionReturnTypes = new Map([...(getContext().activeFunctionReturnTypes ?? new Map())]);
720
+ const bodyStatements = lowerStatementList(ts.isBlock(callbackArg.body) ? callbackArg.body.statements : [], fileName, sourceText, diagnostics, functionReturnTypes, localVariableTypes, fnName, undefined, pointerVars);
721
+ if (!ts.isBlock(callbackArg.body)) {
722
+ const exprText = renderExprAsText(expressionToIR(callbackArg.body, sourceText, diagnostics));
723
+ bodyStatements.push({
724
+ kind: "call",
725
+ sourceSpan: makeSourceSpan(callbackArg.body, fileName, sourceText),
726
+ callee: "__EMIT__",
727
+ args: [{ kind: "string", value: exprText }],
728
+ });
729
+ }
730
+ hoistedNestedFunctions.push({
731
+ originalName: fnName,
732
+ isAsync: false,
733
+ returnType: "void",
734
+ sourceSpan: makeSourceSpan(callbackArg, fileName, sourceText),
735
+ leadingComments: [],
736
+ trailingComments: [],
737
+ parameters: [],
738
+ statements: bodyStatements,
739
+ });
740
+ // Apply the polyfill helper rename (setInterval → __tc_setInterval) so the
741
+ // emitted call matches the runtime helper the framework provides.
742
+ const rawCallee = ts.isIdentifier(call.expression) ? call.expression.text : calleeToText(call.expression);
743
+ const calleeText = rawCallee === "setInterval" ? "__tc_setInterval"
744
+ : rawCallee === "setTimeout" ? "__tc_setTimeout"
745
+ : rawCallee;
746
+ const remainingArgs = call.arguments.slice(1).map((arg) => expressionToIR(arg, sourceText, diagnostics, pointerVars));
747
+ return {
748
+ kind: "call",
749
+ sourceSpan: makeSourceSpan(call, fileName, sourceText),
750
+ leadingComments: comments.leadingComments,
751
+ trailingComments: comments.trailingComments,
752
+ callee: calleeText,
753
+ args: [{ kind: "identifier", value: fnName }, ...remainingArgs],
754
+ };
755
+ }
756
+ // ── Helper: lower a callback expression to C++ text ─────────────────────────
757
+ // (Moved to ui-callback-lowering.ts so onToggle and watchPin share one path,
758
+ // including console.* → platform transform which lived in neither copy.)