@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,808 @@
1
+ import ts from "typescript";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { parseSource } from "../ast/parse.js";
5
+ import { isStringEnum } from "../api/shared/index.js";
6
+ import { makeDiagnostic } from "./ast-node-utils.js";
7
+ import { buildFunctionReturnTypeMap } from "./type-resolution.js";
8
+ import { resolveBoardConstants, tryResolveBoardDefFile } from "./board-resolver.js";
9
+ import { analyzePeripheralUsage, createEmptyPeripheralUsage } from "./peripheral-usage.js";
10
+ import { runProgramValidations } from "./validation-orchestrator.js";
11
+ import { registerFieldMap, hoistedNestedFunctions, hoistedNestedClasses, hoistedNestedEnums, hoistedNestedInterfaces, hoistedNestedTypeAliases, activeNamespaceNames, activeEnumNames, activeStringEnumNames, peripheralAliasMap, pinAliasMap, mcuPinReverseMap, topLevelClassNames, topLevelInterfaceNames, classTypeNames, topLevelClasses, requiredIncludes, resetBuildState, getCurrentBoardConstants, setCurrentBoardConstants, contextStorage, CompilationContext, registeredCallbacks, getContext, discriminatedUnionVariantNames, restParamFunctions, topLevelAliasReceivers } from "./build-ir-state.js";
12
+ import { collectPointerVars, expressionStatementToIR, lowerStatement, variableStatementToIR, prescanArrayUsage, lowerStatementList } from "./statement-to-ir.js";
13
+ import { registerUIModuleImport, registerElementValue } from "./transformers/ui-call-resolver.js";
14
+ import { requireUIHook } from "../ui-hook.js";
15
+ import { autoWireElements, registerScreenId } from "./ui-element-auto-wire.js";
16
+ import { loadHALModules, halInstances, resetHALResolver } from "./hal-resolver.js";
17
+ import { prescanUnsupportedFeatures } from "./feature-prescan.js";
18
+ import { classDeclarationToIR, enumDeclarationToIR, interfaceDeclarationToIR, typeAliasDeclarationToIR } from "./declaration-builders.js";
19
+ import { namespaceToIR } from "./namespace-builder.js";
20
+ import { functionDeclarationToIR, variableAsFunctionToIR } from "./function-builder.js";
21
+ function normalizeEntrypointSyntax(sourceText) {
22
+ return sourceText.replace(/\bfunction\s+void\s*\(/g, "function __cuttlefish_entrypoint__(");
23
+ }
24
+ /**
25
+ * Synthesize forwarding constructors for subclasses that declare none.
26
+ *
27
+ * C++ does not inherit constructors. A TS subclass `class B extends A {}` with
28
+ * no explicit `constructor` previously lowered to a C++ class with NO
29
+ * constructor at all, so `new B(args)` failed (`no matching function for call`)
30
+ * and base parameter-property initialization was skipped. For each such
31
+ * subclass we synthesize a constructor that mirrors the base's parameter
32
+ * signature and forwards every argument via a synthesized `super_call`. If the
33
+ * base is default-constructible (no constructor), we synthesize an empty
34
+ * constructor so `new B()` still works.
35
+ *
36
+ * Bases may live in another file; they are resolved from the local `classes`
37
+ * list first, then from `topLevelClasses` (which carries prebuilt/cross-file
38
+ * ClassIR). Demo #14 Finding B.
39
+ */
40
+ function synthesizeSubclassConstructors(classes) {
41
+ for (const cls of classes) {
42
+ if (cls.constructor || !cls.extendsClass)
43
+ continue;
44
+ // The extendsClass text may carry template args (e.g. "Generic<std::string>");
45
+ // the base class name is the identifier before any '<'.
46
+ const baseName = cls.extendsClass.split("<")[0].trim();
47
+ const base = classes.find((c) => c.name === baseName) ??
48
+ topLevelClasses.get(baseName);
49
+ if (!base)
50
+ continue;
51
+ if (base.constructor && base.constructor.parameters.length > 0) {
52
+ // Mirror the base parameter list, forwarding each as a super_call arg.
53
+ const fwdParams = base.constructor.parameters.map((p) => ({
54
+ name: p.name,
55
+ cppType: p.cppType,
56
+ defaultValue: p.defaultValue,
57
+ isRest: p.isRest,
58
+ ...(p.ownershipKind ? { ownershipKind: p.ownershipKind } : {}),
59
+ }));
60
+ const superArgs = base.constructor.parameters.map((p) => ({
61
+ kind: "identifier",
62
+ value: p.name,
63
+ }));
64
+ const syntheticSpan = {
65
+ filePath: cls.name,
66
+ startOffset: 0,
67
+ endOffset: 0,
68
+ startLine: 1,
69
+ startColumn: 1,
70
+ endLine: 1,
71
+ endColumn: 1,
72
+ };
73
+ cls.constructor = {
74
+ parameters: fwdParams,
75
+ statements: [
76
+ {
77
+ kind: "super_call",
78
+ sourceSpan: syntheticSpan,
79
+ args: superArgs,
80
+ },
81
+ ],
82
+ };
83
+ }
84
+ else {
85
+ // Base is default-constructible; synthesize an empty ctor so the
86
+ // emitter produces `B() { }` rather than nothing.
87
+ cls.constructor = { parameters: [], statements: [] };
88
+ }
89
+ }
90
+ }
91
+ function scanSourceForEnumNames(src) {
92
+ const names = new Set();
93
+ const re = /(?:export\s+)?enum\s+(\w+)\s*\{/g;
94
+ let m;
95
+ while ((m = re.exec(src)) !== null) {
96
+ names.add(m[1]);
97
+ }
98
+ return names;
99
+ }
100
+ function resolveRelativeImportPath(fromFile, moduleSpecifier) {
101
+ // .ui.html modules export no TS symbols (enums/functions) — skip them in the
102
+ // early cross-module pre-scan so they aren't parsed as TypeScript.
103
+ if (moduleSpecifier.endsWith(".ui.html"))
104
+ return undefined;
105
+ let normalized = moduleSpecifier;
106
+ if (normalized.endsWith(".js"))
107
+ normalized = normalized.slice(0, -3) + ".ts";
108
+ else if (normalized.endsWith(".mjs"))
109
+ normalized = normalized.slice(0, -5) + ".ts";
110
+ const basePath = path.resolve(path.dirname(fromFile), normalized);
111
+ const candidates = [
112
+ basePath,
113
+ `${basePath}.ts`,
114
+ `${basePath}.tsx`,
115
+ path.join(basePath, "index.ts"),
116
+ path.join(basePath, "index.tsx"),
117
+ ];
118
+ for (const c of candidates) {
119
+ try {
120
+ if (fs.statSync(c).isFile())
121
+ return c;
122
+ }
123
+ catch { /* skip */ }
124
+ }
125
+ return undefined;
126
+ }
127
+ /** Resolve a relative `.ui.html` import to its absolute path (for UI registration). */
128
+ function resolveUIImportPath(fromFile, moduleSpecifier) {
129
+ if (!moduleSpecifier.startsWith(".") && !moduleSpecifier.startsWith(".."))
130
+ return undefined;
131
+ const basePath = path.resolve(path.dirname(fromFile), moduleSpecifier);
132
+ if (basePath.toLowerCase().endsWith(".ui.html") && requireUIHook().getUIModule(basePath)) {
133
+ return basePath;
134
+ }
135
+ try {
136
+ if (fs.statSync(basePath).isFile() && basePath.toLowerCase().endsWith(".ui.html")) {
137
+ return basePath;
138
+ }
139
+ }
140
+ catch { /* skip */ }
141
+ return undefined;
142
+ }
143
+ export function buildProgramIR(fileName, sourceText, boardPackage, prebuiltClassMap) {
144
+ const parentStrategy = getContext().activeStrategy;
145
+ return contextStorage.run(new CompilationContext(), () => {
146
+ getContext().activeStrategy = parentStrategy;
147
+ loadHALModules(true); // Parse HAL source files (force reload to pick up changes)
148
+ const normalizedSourceText = normalizeEntrypointSyntax(sourceText);
149
+ const source = parseSource(fileName, normalizedSourceText);
150
+ const diagnostics = [];
151
+ // Expose the diagnostics sink on the context so deep IR-lowering helpers
152
+ // (renderExprAsText, HAL fallbacks) that don't receive diagnostics as a
153
+ // parameter can still report unsupported patterns. This is the same array
154
+ // returned in ProgramIR.diagnostics, so throwIfFatalDiagnostics in
155
+ // transpile.ts will abort the build on any error pushed here.
156
+ getContext().diagnostics = diagnostics;
157
+ // Only prescan user code — skip library sources from node_modules and
158
+ // internal packages (the HAL package ships src/ for transpile-time
159
+ // introspection, but its internal use of `any`, Promise, etc. is not
160
+ // user code and should not trigger TS2CPP diagnostics).
161
+ const normalizedFileName = fileName.replace(/\\/g, "/");
162
+ if (!normalizedFileName.includes("/node_modules/") && !normalizedFileName.includes("/packages/")) {
163
+ diagnostics.push(...prescanUnsupportedFeatures(source, normalizedSourceText));
164
+ }
165
+ const imports = [];
166
+ const reExports = [];
167
+ const topLevelStatements = [];
168
+ const functions = [];
169
+ const enums = [];
170
+ const classes = [];
171
+ const typeAliases = [];
172
+ const interfaces = [];
173
+ const namespaces = [];
174
+ const registerClasses = [];
175
+ const typeAliasNodes = new Map();
176
+ const boilerplates = new Set();
177
+ // Reset module-level state for this file
178
+ resetBuildState();
179
+ resetHALResolver();
180
+ registerFieldMap.clear();
181
+ // Phase 0: Pre-scan for top-level classes and register them so type inference can resolve them.
182
+ // Also register classes from other files in the transpile graph so that property accesses
183
+ // on imported class instances are correctly typed (e.g. player.weaponName → std::string).
184
+ if (prebuiltClassMap) {
185
+ for (const [name, classIR] of prebuiltClassMap) {
186
+ topLevelClassNames.add(name);
187
+ classTypeNames.add(name);
188
+ topLevelClasses.set(name, classIR);
189
+ }
190
+ }
191
+ const localClassDeclarations = source.statements.filter(ts.isClassDeclaration);
192
+ for (const statement of localClassDeclarations) {
193
+ if (!statement.name)
194
+ continue;
195
+ topLevelClassNames.add(statement.name.text);
196
+ classTypeNames.add(statement.name.text);
197
+ }
198
+ // Phase 0b: Pre-scan for top-level interface names. Interfaces lower to C++
199
+ // structs (value types), so a variable of an interface type is a value —
200
+ // never null. Registering the names here (before any statement lowering)
201
+ // lets the null-comparison guard in expression-to-ir recognise such values
202
+ // as value types. Demo #18 Finding A.
203
+ for (const statement of source.statements) {
204
+ if (ts.isInterfaceDeclaration(statement) && statement.name) {
205
+ topLevelInterfaceNames.add(statement.name.text);
206
+ }
207
+ }
208
+ for (const statement of localClassDeclarations) {
209
+ // We don't build full IR yet, just enough for type mapping. All local
210
+ // names are already registered so forward and mutually recursive fields
211
+ // use the same class-reference representation.
212
+ const classIR = classDeclarationToIR(statement, fileName, sourceText, [], new Map(), new Map(), [], new Map());
213
+ if (classIR) {
214
+ topLevelClasses.set(classIR.name, classIR);
215
+ }
216
+ }
217
+ const functionReturnTypes = buildFunctionReturnTypeMap(source);
218
+ // Expose return types on the compilation context so UI callbacks / timers
219
+ // can resolve helper types without every call site threading the map.
220
+ getContext().activeFunctionReturnTypes.clear();
221
+ for (const [name, type] of functionReturnTypes) {
222
+ getContext().activeFunctionReturnTypes.set(name, type);
223
+ }
224
+ const topLevelVariableTypes = new Map();
225
+ let defaultExportName;
226
+ // Pre-scan cross-module imports for enum names and function return types
227
+ // so that inferExprCppType can resolve imported enum members and function calls.
228
+ const earlyCrossModuleImports = [];
229
+ for (const stmt of source.statements) {
230
+ if (ts.isImportDeclaration(stmt) && stmt.moduleSpecifier && ts.isStringLiteral(stmt.moduleSpecifier)) {
231
+ const namedImports = [];
232
+ if (stmt.importClause?.namedBindings && ts.isNamedImports(stmt.importClause.namedBindings)) {
233
+ namedImports.push(...stmt.importClause.namedBindings.elements.map((e) => e.name.text));
234
+ }
235
+ earlyCrossModuleImports.push({ moduleSpecifier: stmt.moduleSpecifier.text, namedImports });
236
+ }
237
+ }
238
+ for (const imp of earlyCrossModuleImports) {
239
+ if (!imp.moduleSpecifier.startsWith('./') && !imp.moduleSpecifier.startsWith('../'))
240
+ continue;
241
+ const resolvedPath = resolveRelativeImportPath(fileName, imp.moduleSpecifier);
242
+ if (!resolvedPath)
243
+ continue;
244
+ try {
245
+ const importedSource = fs.readFileSync(resolvedPath, "utf8");
246
+ const importedEnumNames = scanSourceForEnumNames(importedSource);
247
+ for (const name of imp.namedImports) {
248
+ if (importedEnumNames.has(name)) {
249
+ activeEnumNames.add(name);
250
+ }
251
+ }
252
+ const importedParsed = parseSource(resolvedPath, importedSource);
253
+ const importedFnReturnTypes = buildFunctionReturnTypeMap(importedParsed);
254
+ for (const [fnName, returnType] of importedFnReturnTypes) {
255
+ if (imp.namedImports.includes(fnName) && returnType !== "auto") {
256
+ functionReturnTypes.set(fnName, returnType);
257
+ }
258
+ }
259
+ }
260
+ catch {
261
+ // Non-fatal — file might not be readable or parseable
262
+ }
263
+ }
264
+ // Collect pointer variables at top level (for correct -> vs . usage)
265
+ // This also populates activeCArrayVars for typed array variables
266
+ const topLevelPointerVars = collectPointerVars(source.statements);
267
+ for (const statement of source.statements) {
268
+ if (ts.isTypeAliasDeclaration(statement)) {
269
+ typeAliasNodes.set(statement.name.text, statement.type);
270
+ if (ts.isUnionTypeNode(statement.type) && statement.type.types.every(ts.isTypeLiteralNode)) {
271
+ const variantNames = statement.type.types.map((_, i) => `_${statement.name.text}_Variant_${i}`);
272
+ discriminatedUnionVariantNames.set(statement.name.text, variantNames);
273
+ }
274
+ }
275
+ }
276
+ // Pre-scan for mutable arrays (push/pop/indexOf) at top level
277
+ for (const statement of source.statements) {
278
+ prescanArrayUsage(statement);
279
+ }
280
+ // Resolve board-definition constants BEFORE IR building so the HAL resolver
281
+ // can access them via currentBoardConstants during method body processing.
282
+ // First pass: collect imports so we can find the board package.
283
+ const earlyImports = [];
284
+ for (const stmt of source.statements) {
285
+ if (ts.isImportDeclaration(stmt) && stmt.moduleSpecifier && ts.isStringLiteral(stmt.moduleSpecifier)) {
286
+ const moduleSpecifier = stmt.moduleSpecifier.text;
287
+ const namedImports = [];
288
+ if (stmt.importClause?.namedBindings && ts.isNamedImports(stmt.importClause.namedBindings)) {
289
+ namedImports.push(...stmt.importClause.namedBindings.elements.map((e) => e.name.text));
290
+ }
291
+ // The `ui` namespace from @typecad/ui is a compile-time construct: its
292
+ // calls (ui.mount/signal/bind) are intercepted by tryResolveUICall and
293
+ // lowered to IR, but `ui` itself must NOT be emitted as a C++ value
294
+ // (it would synthesize a bogus _ui_t struct). Drop it from the imports
295
+ // IR so the emit path never sees it as an imported value.
296
+ const isUiPackage = moduleSpecifier === '@typecad/ui' || moduleSpecifier === '@typecad/ui';
297
+ const filteredImports = isUiPackage
298
+ ? namedImports.filter((n) => n !== 'ui')
299
+ : namedImports;
300
+ earlyImports.push({ moduleSpecifier, namedImports: filteredImports });
301
+ }
302
+ }
303
+ for (const imp of earlyImports) {
304
+ const boardFile = tryResolveBoardDefFile(fileName, imp.moduleSpecifier, boardPackage);
305
+ if (boardFile) {
306
+ try {
307
+ setCurrentBoardConstants(resolveBoardConstants(boardFile));
308
+ }
309
+ catch {
310
+ // Non-fatal
311
+ }
312
+ break;
313
+ }
314
+ }
315
+ // If only the default board constants are loaded (4 keys from getDefaultBoardConstants),
316
+ // try the config's boardPackage directly. This covers the case where board()/boardResolve()
317
+ // is imported from @typecad/hal but no @typecad/board-* import is present in the user's code.
318
+ const currentBC = getCurrentBoardConstants();
319
+ if (currentBC && currentBC.size <= 4 && boardPackage) {
320
+ const boardFile = tryResolveBoardDefFile(fileName, boardPackage, boardPackage);
321
+ if (boardFile) {
322
+ try {
323
+ setCurrentBoardConstants(resolveBoardConstants(boardFile));
324
+ }
325
+ catch {
326
+ // Non-fatal
327
+ }
328
+ }
329
+ }
330
+ // Phase 0c-bis: register UI module imports (name → .ui.html path) so
331
+ // ui.mount(screen, ...) can resolve `screen` back to its source tree.
332
+ // Only .ui.html specifiers are registered; their named imports are the UI
333
+ // tree handles (e.g. `screen`).
334
+ for (const imp of earlyImports) {
335
+ if (!imp.moduleSpecifier.endsWith(".ui.html"))
336
+ continue;
337
+ const htmlPath = resolveUIImportPath(fileName, imp.moduleSpecifier);
338
+ if (!htmlPath)
339
+ continue;
340
+ for (const name of imp.namedImports) {
341
+ registerUIModuleImport(name, htmlPath);
342
+ // Register each element in the tree for .value access
343
+ const mod = requireUIHook().getUIModule(htmlPath);
344
+ if (mod) {
345
+ // Register screen IDs → indices for <a href="#screenId"> navigation.
346
+ for (let si = 0; si < mod.allStyledScreens.length; si++) {
347
+ const screenNode = mod.allStyledScreens[si];
348
+ if (screenNode.id)
349
+ registerScreenId(screenNode.id, si);
350
+ }
351
+ // Register each element in ALL screens for .value access + auto-wire.
352
+ const collectIds = (node) => {
353
+ if (node.id)
354
+ registerElementValue(name, node.id, htmlPath);
355
+ node.children?.forEach(collectIds);
356
+ };
357
+ if (mod.allStyledScreens.length > 0) {
358
+ let nextUiNodeIndex = 0;
359
+ for (const screen of mod.allStyledScreens) {
360
+ collectIds(screen);
361
+ nextUiNodeIndex = autoWireElements(name, screen, nextUiNodeIndex);
362
+ }
363
+ }
364
+ else {
365
+ collectIds(mod.styled);
366
+ autoWireElements(name, mod.styled, 0);
367
+ }
368
+ }
369
+ }
370
+ }
371
+ // Phase 0d: record top-level HAL alias declarations (varName → receiver text)
372
+ // in a cheap, resolution-free pass. `resolveHALReceiver` follows this map
373
+ // lazily so a function that references an alias (`led.high()`) resolves it
374
+ // even when declared before the `const led = LED.asOutput()` — making HAL
375
+ // resolution order-independent (demo #34 Finding C). Only mode-SETTER calls
376
+ // (which return the pin itself) are recorded; value-bearing reads are
377
+ // excluded so their variables don't alias the pin (Finding B). The receiver
378
+ // is resolved at lookup time (after imports have registered `LED` etc.), not
379
+ // here, so import order doesn't matter.
380
+ const HAL_ALIASING_METHODS = new Set([
381
+ "asOutput", "asInput", "asInputPullUp", "asInputPullDown",
382
+ "output", "inputPullUp", "inputPullDown",
383
+ "device", // I2CBus.device(addr) / SPIBus.device(cs) → device instance
384
+ "tone", // OutputPin.tone(freq) → ToneChain
385
+ "begin", // I2CBus.begin() / SPIBus.begin() / SerialPort.begin() → same instance
386
+ "take", // Bus.take() → same instance (ownership is compile-time only)
387
+ ]);
388
+ for (const node of source.statements) {
389
+ if (!ts.isVariableStatement(node))
390
+ continue;
391
+ for (const decl of node.declarationList.declarations) {
392
+ if (!decl.initializer || !ts.isIdentifier(decl.name))
393
+ continue;
394
+ const init = decl.initializer;
395
+ if (ts.isCallExpression(init) && ts.isPropertyAccessExpression(init.expression)
396
+ && HAL_ALIASING_METHODS.has(init.expression.name.text)
397
+ && ts.isIdentifier(init.expression.expression)) {
398
+ // Capture call arguments for factory methods like device(0x76) that
399
+ // need the arg to construct the derived instance (I2CDevice._address).
400
+ const argTexts = init.arguments.map(a => {
401
+ if (ts.isNumericLiteral(a))
402
+ return a.text;
403
+ if (ts.isStringLiteral(a))
404
+ return a.text;
405
+ if (ts.isIdentifier(a))
406
+ return a.text;
407
+ return a.getText();
408
+ });
409
+ topLevelAliasReceivers.set(decl.name.text, { receiver: init.expression.expression.text, method: init.expression.name.text, args: argTexts });
410
+ }
411
+ }
412
+ }
413
+ source.forEachChild((node) => {
414
+ if (ts.isImportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
415
+ const moduleSpecifier = node.moduleSpecifier.text;
416
+ const namedImports = [];
417
+ let defaultImportName;
418
+ // Handle default import: import X from "./module.js"
419
+ if (node.importClause?.name && ts.isIdentifier(node.importClause.name)) {
420
+ defaultImportName = node.importClause.name.text;
421
+ }
422
+ // Handle named imports: import { a, b } from "./module.js"
423
+ if (node.importClause?.namedBindings && ts.isNamedImports(node.importClause.namedBindings)) {
424
+ namedImports.push(...node.importClause.namedBindings.elements.map((e) => e.name.text));
425
+ }
426
+ if (namedImports.length > 0 || defaultImportName) {
427
+ imports.push({
428
+ moduleSpecifier,
429
+ namedImports,
430
+ ...(defaultImportName ? { defaultImportName } : {}),
431
+ });
432
+ }
433
+ // Register PascalCase imports from relative modules as potential class names
434
+ // so that static method calls (e.g., Button.start()) use the :: operator.
435
+ if (moduleSpecifier.startsWith('./') || moduleSpecifier.startsWith('../')) {
436
+ for (const name of namedImports) {
437
+ if (/^[A-Z]/.test(name)) {
438
+ topLevelClassNames.add(name);
439
+ }
440
+ }
441
+ }
442
+ // Track HAL instances imported from board packages and framework stubs
443
+ // so the HAL resolver can resolve them to Arduino C++ names.
444
+ // The `@typecad/board` virtual import resolves to the board package, so it
445
+ // also registers pin aliases (LED, etc.). Case-insensitive on the scope.
446
+ const lowerSpecifier = moduleSpecifier.toLowerCase();
447
+ const isHALSource = lowerSpecifier.startsWith('@typecad/board-')
448
+ || lowerSpecifier === '@typecad/framework-arduino/arduino'
449
+ || lowerSpecifier === '@typecad/board';
450
+ // UI authoring namespace: `import { ui } from "@typecad/ui"`. The `ui`
451
+ // value is a compile-time construct (its calls are intercepted by
452
+ // tryResolveUICall); it must NOT be emitted as a C++ struct/value.
453
+ // Treat it like the HAL namespace imports (Pulse/Shift/Random): register
454
+ // as a namespace name and skip, so no struct is synthesized.
455
+ if (moduleSpecifier === '@typecad/ui' || moduleSpecifier === '@typecad/ui') {
456
+ for (const name of namedImports) {
457
+ if (name === 'ui') {
458
+ activeNamespaceNames.add(name);
459
+ }
460
+ }
461
+ }
462
+ if (isHALSource) {
463
+ const boardConstants = getCurrentBoardConstants();
464
+ const analogOffset = boardConstants.get("pins.analogOffset") ?? 14;
465
+ for (const name of namedImports) {
466
+ // Check if this name is a known pin alias (D0, A0, LED, etc.)
467
+ // Transitively resolve aliases: LED -> D13 -> "13"
468
+ let pinVal = pinAliasMap.get(name);
469
+ if (pinVal) {
470
+ // Resolve transitive aliases (e.g. LED -> D13 -> 13)
471
+ while (pinVal && pinAliasMap.has(pinVal) && !/^\d+$/.test(pinVal)) {
472
+ const nextVal = pinAliasMap.get(pinVal);
473
+ if (!nextVal || nextVal === pinVal)
474
+ break; // prevent infinite loop
475
+ pinVal = nextVal;
476
+ }
477
+ const fields = new Map([["_pin", pinVal]]);
478
+ const portName = mcuPinReverseMap.get(pinVal);
479
+ if (portName)
480
+ fields.set("_port", portName);
481
+ halInstances.set(name, { className: "Pin", fieldValues: fields });
482
+ continue;
483
+ }
484
+ // D-pins: D0-D53 → Pin instance with MCU port name (fallback)
485
+ const dMatch = name.match(/^D(\d+)$/);
486
+ if (dMatch) {
487
+ const fields = new Map([["_pin", dMatch[1]]]);
488
+ const portName = mcuPinReverseMap.get(dMatch[1]);
489
+ if (portName)
490
+ fields.set("_port", portName);
491
+ halInstances.set(name, { className: "Pin", fieldValues: fields });
492
+ continue;
493
+ }
494
+ // A-pins: A0-A19 → Pin instance with board-specific offset and MCU port name (fallback)
495
+ const aMatch = name.match(/^A(\d+)$/);
496
+ if (aMatch) {
497
+ const pinNum = String(analogOffset + parseInt(aMatch[1]));
498
+ const fields = new Map([["_pin", pinNum]]);
499
+ const portName = mcuPinReverseMap.get(pinNum);
500
+ if (portName)
501
+ fields.set("_port", portName);
502
+ halInstances.set(name, { className: "Pin", fieldValues: fields });
503
+ continue;
504
+ }
505
+ // I2C buses: I2C0, I2C1, ...
506
+ const i2cMatch = name.match(/^I2C(\d+)$/);
507
+ if (i2cMatch) {
508
+ const alias = peripheralAliasMap.get(name) ?? (i2cMatch[1] === '0' ? 'Wire' : `Wire${i2cMatch[1]}`);
509
+ halInstances.set(name, { className: "I2CBus", fieldValues: new Map([["_bus", alias]]) });
510
+ continue;
511
+ }
512
+ // SPI buses: SPI0, SPI1, ...
513
+ const spiMatch = name.match(/^SPI(\d+)$/);
514
+ if (spiMatch) {
515
+ const alias = peripheralAliasMap.get(name) ?? (spiMatch[1] === '0' ? 'SPI' : `SPI${spiMatch[1]}`);
516
+ halInstances.set(name, { className: "SPIBus", fieldValues: new Map([["_bus", alias]]) });
517
+ continue;
518
+ }
519
+ // UART: UART0, UART1, ...
520
+ const uartMatch = name.match(/^UART(\d+)$/);
521
+ if (uartMatch) {
522
+ const alias = peripheralAliasMap.get(name) ?? (uartMatch[1] === '0' ? 'Serial' : `Serial${uartMatch[1]}`);
523
+ halInstances.set(name, { className: "SerialPort", fieldValues: new Map([["_port", alias]]) });
524
+ continue;
525
+ }
526
+ // HAL namespace imports: Pulse, Shift, Random
527
+ if (name === 'Pulse' || name === 'Shift' || name === 'Random') {
528
+ activeNamespaceNames.add(name);
529
+ continue;
530
+ }
531
+ }
532
+ }
533
+ return;
534
+ }
535
+ // Handle re-exports: export * from "./module.js" or export { a, b } from "./module.js"
536
+ if (ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
537
+ const moduleSpecifier = node.moduleSpecifier.text;
538
+ const exportAll = !node.exportClause || !ts.isNamedExports(node.exportClause);
539
+ const namedExports = node.exportClause && ts.isNamedExports(node.exportClause)
540
+ ? node.exportClause.elements.map((e) => e.name.text)
541
+ : undefined;
542
+ reExports.push({
543
+ moduleSpecifier,
544
+ exportAll,
545
+ namedExports,
546
+ });
547
+ return;
548
+ }
549
+ // Handle export default statements (ExportAssignment)
550
+ if (ts.isExportAssignment(node)) {
551
+ if (node.isExportEquals) {
552
+ // export = X — board config style, skip
553
+ return;
554
+ }
555
+ // export default <identifier> — record the name for default import resolution
556
+ if (ts.isIdentifier(node.expression)) {
557
+ defaultExportName = node.expression.text;
558
+ }
559
+ else if (ts.isFunctionExpression(node.expression) && node.expression.name) {
560
+ // export default function name() { ... } — extract the function name.
561
+ defaultExportName = node.expression.name.text;
562
+ }
563
+ else if (ts.isFunctionExpression(node.expression)) {
564
+ // export default function() { ... } (anonymous) — synthesize a name.
565
+ defaultExportName = "__default_export__";
566
+ }
567
+ else if (ts.isClassExpression(node.expression) && node.expression.name) {
568
+ // export default class Name { ... } — extract the class name.
569
+ defaultExportName = node.expression.name.text;
570
+ }
571
+ else if (ts.isClassExpression(node.expression)) {
572
+ // export default class { ... } (anonymous) — synthesize a name.
573
+ defaultExportName = "__default_export__";
574
+ }
575
+ // For export default function/class, the declaration is already processed
576
+ // by the function/class handlers above — we just record the name.
577
+ return;
578
+ }
579
+ // Handle namespace declarations
580
+ if (ts.isModuleDeclaration(node)) {
581
+ const nsIR = namespaceToIR(node, fileName, sourceText, diagnostics, functionReturnTypes, typeAliasNodes, registerClasses, topLevelPointerVars);
582
+ if (nsIR) {
583
+ namespaces.push(nsIR);
584
+ activeNamespaceNames.add(nsIR.name);
585
+ }
586
+ return;
587
+ }
588
+ if (ts.isFunctionDeclaration(node)) {
589
+ const fnIR = functionDeclarationToIR(node, fileName, sourceText, diagnostics, functionReturnTypes, typeAliasNodes, boilerplates, topLevelPointerVars, lowerStatementList);
590
+ if (fnIR) {
591
+ functions.push(fnIR);
592
+ }
593
+ return;
594
+ }
595
+ if (ts.isVariableStatement(node)) {
596
+ // Check if all declarations are function expressions/arrow functions
597
+ const fnResults = variableAsFunctionToIR(node, fileName, sourceText, diagnostics, functionReturnTypes, typeAliasNodes, boilerplates, topLevelPointerVars, lowerStatementList);
598
+ if (fnResults) {
599
+ functions.push(...fnResults);
600
+ return;
601
+ }
602
+ topLevelStatements.push(...variableStatementToIR(node, fileName, sourceText, diagnostics, functionReturnTypes, topLevelVariableTypes, typeAliasNodes, topLevelPointerVars, "", lowerStatementList));
603
+ return;
604
+ }
605
+ if (ts.isExpressionStatement(node)) {
606
+ const lowered = expressionStatementToIR(node, fileName, sourceText, diagnostics, functionReturnTypes, topLevelVariableTypes, topLevelPointerVars);
607
+ if (lowered) {
608
+ topLevelStatements.push(lowered);
609
+ }
610
+ return;
611
+ }
612
+ if (ts.isClassDeclaration(node)) {
613
+ const classIR = classDeclarationToIR(node, fileName, sourceText, diagnostics, functionReturnTypes, typeAliasNodes, registerClasses, topLevelPointerVars);
614
+ if (classIR) {
615
+ classes.push(classIR);
616
+ topLevelClassNames.add(classIR.name);
617
+ classTypeNames.add(classIR.name);
618
+ topLevelClasses.set(classIR.name, classIR);
619
+ }
620
+ return;
621
+ }
622
+ // Handle enum declarations
623
+ if (ts.isEnumDeclaration(node)) {
624
+ const enumIR = enumDeclarationToIR(node, fileName, sourceText);
625
+ if (enumIR) {
626
+ enums.push(enumIR);
627
+ activeEnumNames.add(enumIR.name);
628
+ if (isStringEnum(enumIR)) {
629
+ activeStringEnumNames.add(enumIR.name);
630
+ }
631
+ }
632
+ return;
633
+ }
634
+ // Handle interface declarations
635
+ if (ts.isInterfaceDeclaration(node)) {
636
+ const ifaceIR = interfaceDeclarationToIR(node, fileName, sourceText, typeAliasNodes);
637
+ if (ifaceIR) {
638
+ interfaces.push(ifaceIR);
639
+ }
640
+ return;
641
+ }
642
+ // Handle type alias declarations
643
+ if (ts.isTypeAliasDeclaration(node)) {
644
+ const aliasIR = typeAliasDeclarationToIR(node, fileName, sourceText, typeAliasNodes);
645
+ if (aliasIR) {
646
+ typeAliases.push(aliasIR);
647
+ }
648
+ return;
649
+ }
650
+ if (node.kind === ts.SyntaxKind.EndOfFileToken) {
651
+ return;
652
+ }
653
+ // General fallthrough: lower any remaining statement types (while, for,
654
+ // if, switch, do-while, etc.) that appear at the top level directly.
655
+ {
656
+ const lowered = lowerStatement(node, fileName, sourceText, diagnostics, functionReturnTypes, topLevelVariableTypes, "<top-level>", typeAliasNodes, topLevelPointerVars);
657
+ if (lowered) {
658
+ topLevelStatements.push(...lowered);
659
+ return;
660
+ }
661
+ }
662
+ diagnostics.push(makeDiagnostic(normalizedSourceText, node.pos, "Top-level node currently unsupported and skipped.", "error", "TS2CPP_UNSUPPORTED_TOPLEVEL"));
663
+ });
664
+ // Collect any nested functions that were hoisted during IR building
665
+ functions.push(...hoistedNestedFunctions);
666
+ // Collect any nested classes that were hoisted during IR building
667
+ classes.push(...hoistedNestedClasses);
668
+ // Synthesize forwarding constructors for subclasses that don't declare one.
669
+ // A subclass `class B extends A {}` with no explicit constructor previously
670
+ // emitted NO constructor, so `new B(args)` failed (no matching ctor) and any
671
+ // base parameter-property initialization was lost. C++ has no implicit
672
+ // inherited constructor: we must synthesize one that mirrors the base's
673
+ // signature and forwards via a super_call. See demo #14 Finding B.
674
+ synthesizeSubclassConstructors(classes);
675
+ // Collect any nested enums that were hoisted during IR building
676
+ enums.push(...hoistedNestedEnums);
677
+ // Collect any local interface declarations that were hoisted during IR building
678
+ for (const iface of hoistedNestedInterfaces) {
679
+ if (!interfaces.some(i => i.name === iface.name)) {
680
+ interfaces.push(iface);
681
+ }
682
+ }
683
+ // Collect any local type alias declarations that were hoisted during IR building
684
+ for (const alias of hoistedNestedTypeAliases) {
685
+ if (!typeAliases.some(a => a.name === alias.name)) {
686
+ typeAliases.push(alias);
687
+ }
688
+ }
689
+ // Merge duplicate interface declarations (TS declaration merging)
690
+ {
691
+ const ifaceMap = new Map();
692
+ for (const iface of interfaces) {
693
+ const existing = ifaceMap.get(iface.name);
694
+ if (existing) {
695
+ existing.fields.push(...iface.fields);
696
+ existing.methods.push(...iface.methods);
697
+ }
698
+ else {
699
+ ifaceMap.set(iface.name, { ...iface, fields: [...iface.fields], methods: [...iface.methods] });
700
+ }
701
+ }
702
+ interfaces.length = 0;
703
+ interfaces.push(...ifaceMap.values());
704
+ }
705
+ // Record top-level interface names. Interfaces lower to C++ structs (value
706
+ // types), so a local/param of an interface type is a value — never null. The
707
+ // null-comparison guard in expression-to-ir consults this set to recognise
708
+ // such a value as a value type (topLevelClassNames holds only classes, which
709
+ // are always pointer/reference types). Demo #18 Finding A.
710
+ for (const iface of interfaces) {
711
+ topLevelInterfaceNames.add(iface.name);
712
+ }
713
+ // Merge duplicate namespace declarations
714
+ {
715
+ const nsMap = new Map();
716
+ for (const ns of namespaces) {
717
+ const existing = nsMap.get(ns.name);
718
+ if (existing) {
719
+ existing.enums.push(...ns.enums);
720
+ existing.classes.push(...ns.classes);
721
+ existing.functions.push(...ns.functions);
722
+ existing.constants.push(...ns.constants);
723
+ existing.interfaces.push(...ns.interfaces);
724
+ existing.typeAliases.push(...ns.typeAliases);
725
+ }
726
+ else {
727
+ nsMap.set(ns.name, {
728
+ ...ns,
729
+ enums: [...ns.enums],
730
+ classes: [...ns.classes],
731
+ functions: [...ns.functions],
732
+ constants: [...ns.constants],
733
+ interfaces: [...ns.interfaces],
734
+ typeAliases: [...ns.typeAliases],
735
+ });
736
+ }
737
+ }
738
+ namespaces.length = 0;
739
+ namespaces.push(...nsMap.values());
740
+ }
741
+ // Populate top-level class names for :: static method rendering
742
+ for (const cls of classes) {
743
+ topLevelClassNames.add(cls.name);
744
+ classTypeNames.add(cls.name);
745
+ }
746
+ // Board constants were already resolved before IR building (for HAL resolver access).
747
+ // Reuse the module-level value for the ProgramIR output.
748
+ const boardConstants = getCurrentBoardConstants();
749
+ // Analyze peripheral usage for optimization
750
+ let peripheralUsage;
751
+ try {
752
+ const partialProgram = {
753
+ fileName,
754
+ imports,
755
+ reExports,
756
+ structs: [],
757
+ enums,
758
+ classes,
759
+ typeAliases,
760
+ registerClasses,
761
+ topLevelStatements,
762
+ functions,
763
+ boilerplates,
764
+ diagnostics,
765
+ boardConstants,
766
+ peripheralUsage: createEmptyPeripheralUsage(),
767
+ interfaces,
768
+ namespaces,
769
+ // Include registeredCallbacks so interrupt-safety analysis can see
770
+ // user ISR lambdas (onRising/onFalling/etc.) at validation time. The
771
+ // callbackIR on each entry carries isInterruptHandler=true (set in
772
+ // hal-emitter.ts); without this field the scanner's source (3) at
773
+ // interrupt-analysis.ts:212 never iterates, so ISR unsafe-op detection
774
+ // silently misses every user interrupt handler.
775
+ registeredCallbacks: [...registeredCallbacks],
776
+ };
777
+ peripheralUsage = analyzePeripheralUsage(partialProgram);
778
+ partialProgram.peripheralUsage = peripheralUsage;
779
+ diagnostics.push(...runProgramValidations(partialProgram));
780
+ }
781
+ catch (e) {
782
+ // If peripheral analysis fails, use empty usage
783
+ peripheralUsage = createEmptyPeripheralUsage();
784
+ }
785
+ return {
786
+ fileName,
787
+ imports,
788
+ reExports,
789
+ structs: [],
790
+ enums,
791
+ classes,
792
+ typeAliases,
793
+ registerClasses,
794
+ topLevelStatements,
795
+ functions,
796
+ boilerplates,
797
+ diagnostics,
798
+ boardConstants,
799
+ peripheralUsage,
800
+ requiredIncludes: new Set(requiredIncludes),
801
+ interfaces,
802
+ namespaces,
803
+ registeredCallbacks: [...registeredCallbacks],
804
+ restParamFunctions: new Map(restParamFunctions),
805
+ ...(defaultExportName ? { defaultExportName } : {}),
806
+ };
807
+ });
808
+ }