@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,735 @@
1
+ import ts from "typescript";
2
+ import { requiredIncludes, registeredCallbacks, activeStringVars, TYPED_ARRAY_ELEMENT_MAP, getContext, floatVariables, halInstances, getCurrentBoardConstants } from "../build-ir-state.js";
3
+ import { getCurrentIrTypeScope } from "../symbol-types.js";
4
+ import { renderExprAsText } from "../render-expr.js";
5
+ import { escapeCppKeyword } from "../../utils/strings.js";
6
+ import { halClassRegistry, halGlobalFunctions } from "./hal-parser.js";
7
+ import { tryResolveSemanticCall, tryResolveBoardResolveArg, tryResolveCompoundSemanticReturn, resolveConcatPath } from "./hal-plugins.js";
8
+ /** Escape C++ keywords in resolved text, but only when the text looks like a
9
+ * variable reference (not a literal like "false", "true", "42", or a string). */
10
+ export function maybeEscapeResolvedText(text) {
11
+ // Skip escaping for boolean literals, numeric literals, and string literals
12
+ if (text === "true" || text === "false" || text === "null" || text === "undefined")
13
+ return text;
14
+ if (/^-?\d+(\.\d+)?$/.test(text))
15
+ return text;
16
+ if (text.startsWith('"') || text.startsWith("'"))
17
+ return text;
18
+ return escapeCppKeyword(text);
19
+ }
20
+ /**
21
+ * Strip a leading `.c_str()` from the literal text that follows a template span
22
+ * when the resolved value is a C++ string literal (e.g. `"label"`).
23
+ *
24
+ * HAL authors write `${param}.c_str()` to convert a `String`/`std::string`
25
+ * variable to `const char*` for C APIs. That is correct when `param` is a
26
+ * variable (renders as `myVar` → `myVar.c_str()`). But when `param` is a string
27
+ * literal it renders as `"label"`, and `"label".c_str()` is invalid C++ — a
28
+ * string literal has no `.c_str()` member. This drops the conversion in that
29
+ * case so `"label".c_str()` becomes just `"label"`, while leaving the variable
30
+ * path untouched.
31
+ */
32
+ export function stripCStrAfterStringLiteral(followingLiteralText, resolvedValue) {
33
+ if (resolvedValue.startsWith('"') && followingLiteralText.startsWith(".c_str()")) {
34
+ return followingLiteralText.slice(".c_str()".length);
35
+ }
36
+ return followingLiteralText;
37
+ }
38
+ /** Map a TypeScript PrefixUnaryExpression operator SyntaxKind to C++ text. */
39
+ function prefixOperatorText(operator) {
40
+ switch (operator) {
41
+ case ts.SyntaxKind.ExclamationToken: return "!";
42
+ case ts.SyntaxKind.PlusToken: return "+";
43
+ case ts.SyntaxKind.MinusToken: return "-";
44
+ case ts.SyntaxKind.TildeToken: return "~";
45
+ case ts.SyntaxKind.PlusPlusToken: return "++";
46
+ case ts.SyntaxKind.MinusMinusToken: return "--";
47
+ default: return "";
48
+ }
49
+ }
50
+ /**
51
+ * Inline a `this.<method>()` getter call used inside a compound expression
52
+ * (e.g. `!this.read()` in InputPin.isLow()). The InputPin boolean-query
53
+ * methods all reduce to a semantic read over `this._pin`; mapping them here
54
+ * keeps `this` out of the emitted free function. Returns null if the method
55
+ * is not a recognized inlinable getter.
56
+ */
57
+ function inlineThisGetterCall(methodName, pin) {
58
+ switch (methodName) {
59
+ case "read":
60
+ return `digitalRead(${pin})`;
61
+ case "readAnalog":
62
+ return `analogRead(${pin})`;
63
+ case "isHigh":
64
+ return `digitalRead(${pin})`;
65
+ case "isLow":
66
+ return `(!digitalRead(${pin}))`;
67
+ default:
68
+ return null;
69
+ }
70
+ }
71
+ /** Resolve an arbitrary expression to its text form, with this/param substitution. */
72
+ export function resolveExpressionText(expr, instance, paramNames, callArgTexts, paramDefaults) {
73
+ // this._field → look up in instance
74
+ // OtherInstance._field → look up in tracked halInstances (cross-instance reference)
75
+ if (ts.isPropertyAccessExpression(expr)) {
76
+ const isThis = expr.expression.kind === ts.SyntaxKind.ThisKeyword
77
+ || (ts.isIdentifier(expr.expression) && expr.expression.text === "this")
78
+ || expr.expression.getText() === "this";
79
+ if (isThis) {
80
+ const fieldName = expr.name.text;
81
+ const val = instance.fieldValues.get(fieldName) ?? instance.fieldValues.get(fieldName.startsWith("_") ? fieldName.slice(1) : "_" + fieldName);
82
+ if (val !== undefined && val !== null)
83
+ return val;
84
+ // Fallback: try to see if it's a known field that should be mapped
85
+ if (fieldName === "_pin" && instance.fieldValues.has("pin"))
86
+ return instance.fieldValues.get("pin");
87
+ if (fieldName === "_bus" && instance.fieldValues.has("bus"))
88
+ return instance.fieldValues.get("bus");
89
+ return `this->${fieldName}`;
90
+ }
91
+ // Cross-instance field reference: e.g. ADC._reference → look up tracked instance
92
+ if (ts.isIdentifier(expr.expression)) {
93
+ let crossInst = halInstances.get(expr.expression.text);
94
+ // Fall back to bare-name defaults if not yet cached
95
+ if (!crossInst && expr.expression.text === "ADC") {
96
+ crossInst = { className: "ADCClass", fieldValues: new Map([["_reference", "DEFAULT"]]) };
97
+ }
98
+ if (crossInst) {
99
+ const val = crossInst.fieldValues.get(expr.name.text);
100
+ if (val !== undefined)
101
+ return val;
102
+ }
103
+ }
104
+ const obj = resolveExpressionText(expr.expression, instance, paramNames, callArgTexts, paramDefaults);
105
+ if (obj === null)
106
+ return null;
107
+ return `${obj}.${expr.name.text}`;
108
+ }
109
+ // Identifier → parameter or constant
110
+ if (ts.isIdentifier(expr)) {
111
+ const paramIdx = paramNames.indexOf(expr.text);
112
+ if (paramIdx !== -1) {
113
+ if (paramIdx < callArgTexts.length) {
114
+ const isSpread = expr.text === instance._spreadParamName;
115
+ if (isSpread) {
116
+ const spreadArgs = callArgTexts.slice(paramIdx);
117
+ return spreadArgs.join(", ");
118
+ }
119
+ return maybeEscapeResolvedText(callArgTexts[paramIdx]);
120
+ }
121
+ else if (paramDefaults?.has(expr.text)) {
122
+ return maybeEscapeResolvedText(paramDefaults.get(expr.text));
123
+ }
124
+ // Optional parameter that was omitted at the call site and has no
125
+ // default: its runtime value is `undefined`. Returning the literal name
126
+ // would leak a dangling identifier into the emitted C++ (e.g.
127
+ // `asOutput(initial?: ...)` referenced as `(initial) ? HIGH : LOW`).
128
+ return "undefined";
129
+ }
130
+ return escapeCppKeyword(expr.text);
131
+ }
132
+ if (ts.isNumericLiteral(expr))
133
+ return expr.text;
134
+ if (ts.isStringLiteral(expr))
135
+ return expr.text;
136
+ // Call expression (e.g., digitalRead(this._pin), board("path"))
137
+ if (ts.isCallExpression(expr)) {
138
+ // callback(param) → return the patched placeholder text
139
+ if (ts.isIdentifier(expr.expression) && expr.expression.text === "callback") {
140
+ const cbArg = expr.arguments[0];
141
+ if (cbArg && ts.isIdentifier(cbArg)) {
142
+ const paramIdx = paramNames.indexOf(cbArg.text);
143
+ if (paramIdx !== -1 && paramIdx < callArgTexts.length) {
144
+ return callArgTexts[paramIdx];
145
+ }
146
+ }
147
+ return null;
148
+ }
149
+ // String(x) → return resolved text of x (identity in compile-time string context)
150
+ if (ts.isIdentifier(expr.expression) && expr.expression.text === "String") {
151
+ const inner = expr.arguments[0];
152
+ if (inner) {
153
+ return resolveExpressionText(inner, instance, paramNames, callArgTexts, paramDefaults);
154
+ }
155
+ return "";
156
+ }
157
+ // board("path") → look up in current board constants
158
+ if (ts.isIdentifier(expr.expression) && expr.expression.text === "board") {
159
+ const pathArg = expr.arguments[0];
160
+ // Static string literal path: board("peripherals.adc.0.resolution")
161
+ if (pathArg && ts.isStringLiteral(pathArg)) {
162
+ const bc = getCurrentBoardConstants();
163
+ if (bc) {
164
+ const val = bc.get(pathArg.text);
165
+ if (val !== undefined)
166
+ return String(val);
167
+ }
168
+ }
169
+ // Dynamic path via string concat or template literal:
170
+ // board("prefix." + this._field) / board(`prefix.${this._field}`)
171
+ if (pathArg && (ts.isBinaryExpression(pathArg) || ts.isTemplateExpression(pathArg) || ts.isStringLiteral(pathArg) || ts.isNoSubstitutionTemplateLiteral(pathArg))) {
172
+ const fullPath = resolveConcatPath(pathArg, instance, paramNames, callArgTexts, paramDefaults);
173
+ if (fullPath !== null) {
174
+ const bc = getCurrentBoardConstants();
175
+ if (bc) {
176
+ const val = bc.get(fullPath);
177
+ if (val !== undefined)
178
+ return String(val);
179
+ }
180
+ }
181
+ }
182
+ return null;
183
+ }
184
+ const callee = resolveExpressionText(expr.expression, instance, paramNames, callArgTexts, paramDefaults);
185
+ if (callee === null)
186
+ return null;
187
+ const args = expr.arguments.map(arg => resolveExpressionText(arg, instance, paramNames, callArgTexts, paramDefaults));
188
+ if (args.some(a => a === null))
189
+ return null;
190
+ // this.<method>() — a HAL method called on the same instance inside a
191
+ // compound expression (e.g. InputPin.isLow() returns `!this.read()`).
192
+ // Inline the known InputPin boolean-query getters over the resolved pin
193
+ // so `this` never leaks into the emitted free function.
194
+ if (ts.isPropertyAccessExpression(expr.expression)) {
195
+ const isThisAccess = expr.expression.expression.kind === ts.SyntaxKind.ThisKeyword
196
+ || (ts.isIdentifier(expr.expression.expression) && expr.expression.expression.text === "this")
197
+ || expr.expression.expression.getText() === "this";
198
+ if (isThisAccess) {
199
+ const methodName = expr.expression.name.text;
200
+ const pin = instance.fieldValues.get("_pin") ?? instance.fieldValues.get("pin");
201
+ if (pin !== undefined) {
202
+ const inlined = inlineThisGetterCall(methodName, pin);
203
+ if (inlined !== null)
204
+ return inlined;
205
+ }
206
+ }
207
+ }
208
+ return `${callee}(${args.join(", ")})`;
209
+ }
210
+ // Binary expression
211
+ if (ts.isBinaryExpression(expr)) {
212
+ const left = resolveExpressionText(expr.left, instance, paramNames, callArgTexts, paramDefaults);
213
+ const right = resolveExpressionText(expr.right, instance, paramNames, callArgTexts, paramDefaults);
214
+ if (left === null || right === null)
215
+ return null;
216
+ let op = expr.operatorToken.getText();
217
+ if (op === "===")
218
+ op = "==";
219
+ else if (op === "!==")
220
+ op = "!=";
221
+ else if (op === "??") {
222
+ // If the left side is a literal expression (true/false/number/string),
223
+ // it can never be undefined so use it directly
224
+ if (expr.left.kind === ts.SyntaxKind.TrueKeyword || expr.left.kind === ts.SyntaxKind.FalseKeyword ||
225
+ ts.isNumericLiteral(expr.left) || ts.isStringLiteral(expr.left)) {
226
+ return left;
227
+ }
228
+ // If the left side is an identifier whose resolved text matches its source,
229
+ // it means the parameter was not provided by the caller, so use the right side
230
+ if (ts.isIdentifier(expr.left) && left === expr.left.text) {
231
+ return right;
232
+ }
233
+ // If the left side resolved to a recognized literal (true, false, or number),
234
+ // the parameter was provided with a concrete value, not undefined.
235
+ if (left === "true" || left === "false" || /^-?\d+(\.\d+)?$/.test(left)) {
236
+ return left;
237
+ }
238
+ // Use a more concise ternary for C++
239
+ return `(${left} != CUTTLEFISH_UNDEFINED ? ${left} : ${right})`;
240
+ }
241
+ return `${left} ${op} ${right}`;
242
+ }
243
+ // Parenthesized expression: (expr) → unwrap to inner expression
244
+ if (ts.isParenthesizedExpression(expr)) {
245
+ return resolveExpressionText(expr.expression, instance, paramNames, callArgTexts, paramDefaults);
246
+ }
247
+ // Type assertion: this as any → unwrap to inner expression
248
+ if (ts.isAsExpression(expr)) {
249
+ return resolveExpressionText(expr.expression, instance, paramNames, callArgTexts, paramDefaults);
250
+ }
251
+ // Template expression: `text ${expr} more text`
252
+ if (ts.isTemplateExpression(expr)) {
253
+ let result = expr.head.text;
254
+ for (const span of expr.templateSpans) {
255
+ const resolved = resolveExpressionText(span.expression, instance, paramNames, callArgTexts, paramDefaults);
256
+ if (resolved === null)
257
+ return null;
258
+ result += resolved + stripCStrAfterStringLiteral(span.literal.text, resolved);
259
+ }
260
+ return result;
261
+ }
262
+ // No-substitution template literal: `text`
263
+ if (ts.isNoSubstitutionTemplateLiteral(expr)) {
264
+ return expr.text;
265
+ }
266
+ // Prefix unary expression (e.g. `!this.read()`): resolve the operand and
267
+ // re-apply the operator. Without this, `!this.read()` falls through to the
268
+ // raw getText() fallback and leaks `this` into a free function.
269
+ if (ts.isPrefixUnaryExpression(expr)) {
270
+ const operand = resolveExpressionText(expr.operand, instance, paramNames, callArgTexts, paramDefaults);
271
+ if (operand === null)
272
+ return null;
273
+ const op = prefixOperatorText(expr.operator);
274
+ return `${op}${operand}`;
275
+ }
276
+ return expr.getText ? expr.getText() : null;
277
+ }
278
+ /** Scan an expression AST for callback() calls, extract callback IR from callArgs,
279
+ * register them, and patch callArgTexts with placeholder names. */
280
+ export function extractAndRegisterCallbacks(expr, paramNames, callArgs, callArgTexts) {
281
+ function scan(node) {
282
+ if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "callback") {
283
+ const cbArg = node.arguments[0];
284
+ if (cbArg && ts.isIdentifier(cbArg)) {
285
+ const paramIdx = paramNames.indexOf(cbArg.text);
286
+ if (paramIdx !== -1 && paramIdx < callArgs.length) {
287
+ const callbackIR = callArgs[paramIdx];
288
+ if (callbackIR.kind === "callback" || callbackIR.kind === "lambda") {
289
+ const placeholder = `__CALLBACK_${getContext().callbackPlaceholderCounter++}__`;
290
+ const normalized = callbackIR.kind === "lambda"
291
+ ? {
292
+ kind: "callback",
293
+ params: callbackIR.params.map((p) => p.name),
294
+ statements: callbackIR.body,
295
+ sourceSpan: callbackIR.body[0]?.sourceSpan ?? {
296
+ filePath: "",
297
+ startLine: 0,
298
+ startColumn: 0,
299
+ endLine: 0,
300
+ endColumn: 0,
301
+ startOffset: 0,
302
+ endOffset: 0,
303
+ },
304
+ isInterruptHandler: true,
305
+ }
306
+ : callbackIR;
307
+ normalized.isInterruptHandler = true;
308
+ registeredCallbacks.push({ placeholderName: placeholder, callbackIR: normalized });
309
+ callArgTexts[paramIdx] = placeholder;
310
+ }
311
+ }
312
+ }
313
+ }
314
+ if (ts.isTemplateExpression(node)) {
315
+ for (const span of node.templateSpans) {
316
+ scan(span.expression);
317
+ }
318
+ }
319
+ }
320
+ scan(expr);
321
+ }
322
+ /** Process a HAL method body, resolving emit()/include()/semantic calls.
323
+ * Returns { emitLines, halOps, returnValue, returnClassName } or null if unresolvable.
324
+ *
325
+ * `emitLines` contains legacy raw C++ strings (from `emit()` calls).
326
+ * `halOps` contains structured HALOpIR nodes (from semantic function calls like
327
+ * `gpioWrite()`, `i2cBegin()`, etc.).
328
+ *
329
+ * During migration both can coexist; consumers should prefer `halOps` when present. */
330
+ export function processHALMethodBody(instance, methodName, callArgs) {
331
+ let methodEntry;
332
+ if (instance.className) {
333
+ const classEntry = halClassRegistry.get(instance.className);
334
+ if (!classEntry)
335
+ return null;
336
+ methodEntry = classEntry.methods.get(methodName);
337
+ // Fallback: search other HAL classes for the method (e.g., Pin instance calling InputPin.onFalling)
338
+ if (!methodEntry) {
339
+ for (const [, entry] of halClassRegistry) {
340
+ const found = entry.methods.get(methodName);
341
+ if (found && found.methodNode.body) {
342
+ methodEntry = found;
343
+ break;
344
+ }
345
+ }
346
+ }
347
+ }
348
+ else {
349
+ methodEntry = halGlobalFunctions.get(methodName);
350
+ }
351
+ if (!methodEntry || !methodEntry.methodNode.body)
352
+ return null;
353
+ const paramNames = methodEntry.paramNames;
354
+ const spreadParamName = methodEntry.spreadParamName;
355
+ const callArgTexts = callArgs.map(a => renderExprAsText(a));
356
+ instance._spreadParamName = spreadParamName;
357
+ const paramDefaults = methodEntry.paramDefaults;
358
+ const body = methodEntry.methodNode.body;
359
+ const emitLines = [];
360
+ const halOps = [];
361
+ // For string_concat args (template literals), generate snprintf instead of
362
+ // C++ + concatenation which is invalid for char* on Arduino.
363
+ for (let i = 0; i < callArgs.length; i++) {
364
+ const arg = callArgs[i];
365
+ if (arg.kind === "string_concat" && arg.parts.some(p => p.kind !== "string")) {
366
+ const snprintf = buildSnprintfFromConcat(arg);
367
+ if (snprintf) {
368
+ emitLines.push(...snprintf.lines);
369
+ callArgTexts[i] = snprintf.bufferName;
370
+ }
371
+ }
372
+ }
373
+ let returnValue;
374
+ for (const stmt of body.statements) {
375
+ // this._field = param — track field updates on the instance
376
+ if (ts.isExpressionStatement(stmt) &&
377
+ ts.isBinaryExpression(stmt.expression) &&
378
+ stmt.expression.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
379
+ const left = stmt.expression.left;
380
+ const right = stmt.expression.right;
381
+ if (ts.isPropertyAccessExpression(left) &&
382
+ (left.expression.kind === ts.SyntaxKind.ThisKeyword ||
383
+ (ts.isIdentifier(left.expression) && left.expression.text === "this"))) {
384
+ const fieldName = left.name.text;
385
+ const resolved = resolveExpressionText(right, instance, paramNames, callArgTexts, paramDefaults);
386
+ if (resolved !== null) {
387
+ instance.fieldValues.set(fieldName, resolved);
388
+ }
389
+ }
390
+ }
391
+ // emit(...) call or semantic HAL function call
392
+ if (ts.isExpressionStatement(stmt) && ts.isCallExpression(stmt.expression)) {
393
+ const call = stmt.expression;
394
+ // ── Board resolve: pure lookup, no side effects — skip ──
395
+ if (ts.isIdentifier(call.expression) && call.expression.text === "boardResolve") {
396
+ continue;
397
+ }
398
+ // ── Semantic HAL function calls (gpioWrite, i2cBegin, etc.) ──
399
+ if (ts.isIdentifier(call.expression)) {
400
+ // Extract callbacks from semantic call arguments and patch callArgTexts
401
+ // with placeholder names before resolving (mirrors the emit() path).
402
+ for (const arg of call.arguments) {
403
+ extractAndRegisterCallbacks(arg, paramNames, callArgs, callArgTexts);
404
+ }
405
+ const semanticOp = tryResolveSemanticCall(call.expression.text, call.arguments, instance, paramNames, callArgTexts, paramDefaults, callArgs);
406
+ if (semanticOp) {
407
+ halOps.push(semanticOp);
408
+ continue;
409
+ }
410
+ }
411
+ // include(...) call
412
+ if (ts.isIdentifier(call.expression) && call.expression.text === "include") {
413
+ const firstArg = call.arguments[0];
414
+ if (firstArg && ts.isStringLiteral(firstArg)) {
415
+ requiredIncludes.add(firstArg.text);
416
+ }
417
+ continue;
418
+ }
419
+ }
420
+ // R4: if statement with compile-time condition evaluation
421
+ if (ts.isIfStatement(stmt)) {
422
+ let conditionTrue = true; // default: process then-branch
423
+ const cond = stmt.expression;
424
+ if (cond && ts.isBinaryExpression(cond)) {
425
+ const left = resolveExpressionText(cond.left, instance, paramNames, callArgTexts, paramDefaults);
426
+ const right = resolveExpressionText(cond.right, instance, paramNames, callArgTexts, paramDefaults);
427
+ if (left !== null && right !== null) {
428
+ const op = cond.operatorToken.kind;
429
+ if (op === ts.SyntaxKind.EqualsEqualsToken || op === ts.SyntaxKind.EqualsEqualsEqualsToken) {
430
+ conditionTrue = left === right;
431
+ }
432
+ else if (op === ts.SyntaxKind.ExclamationEqualsToken || op === ts.SyntaxKind.ExclamationEqualsEqualsToken) {
433
+ conditionTrue = left !== right;
434
+ }
435
+ }
436
+ }
437
+ if (conditionTrue && stmt.thenStatement) {
438
+ const returnRef = returnValue !== undefined ? undefined : { value: "" };
439
+ processStatementList(ts.isBlock(stmt.thenStatement) ? stmt.thenStatement.statements : [stmt.thenStatement], instance, paramNames, callArgTexts, emitLines, halOps, callArgs, paramDefaults, returnRef);
440
+ if (returnRef && returnRef.value)
441
+ returnValue = returnRef.value;
442
+ }
443
+ if (!conditionTrue && stmt.elseStatement) {
444
+ const returnRef = returnValue !== undefined ? undefined : { value: "" };
445
+ processStatementList(ts.isBlock(stmt.elseStatement) ? stmt.elseStatement.statements : [stmt.elseStatement], instance, paramNames, callArgTexts, emitLines, halOps, callArgs, paramDefaults, returnRef);
446
+ if (returnRef && returnRef.value)
447
+ returnValue = returnRef.value;
448
+ }
449
+ continue;
450
+ }
451
+ // R2: return expr — check for semantic call first, then fall back to resolveExpressionText
452
+ if (ts.isReturnStatement(stmt) && stmt.expression && returnValue === undefined) {
453
+ const retExpr = stmt.expression;
454
+ // ── Board resolve: constant-fold via board constants ──
455
+ if (ts.isCallExpression(retExpr) && ts.isIdentifier(retExpr.expression) && retExpr.expression.text === "boardResolve") {
456
+ const resolved = tryResolveBoardResolveArg(retExpr.arguments, instance, paramNames, callArgTexts, paramDefaults);
457
+ if (resolved !== null) {
458
+ const bc = getCurrentBoardConstants();
459
+ const val = bc?.get(resolved);
460
+ if (val !== undefined) {
461
+ returnValue = String(val);
462
+ continue;
463
+ }
464
+ }
465
+ }
466
+ let actualRetExpr = retExpr;
467
+ while (ts.isAsExpression(actualRetExpr) || ts.isParenthesizedExpression(actualRetExpr)) {
468
+ actualRetExpr = actualRetExpr.expression;
469
+ }
470
+ if (ts.isCallExpression(actualRetExpr) && ts.isIdentifier(actualRetExpr.expression)) {
471
+ for (const arg of actualRetExpr.arguments) {
472
+ extractAndRegisterCallbacks(arg, paramNames, callArgs, callArgTexts);
473
+ }
474
+ const semanticOp = tryResolveSemanticCall(actualRetExpr.expression.text, actualRetExpr.arguments, instance, paramNames, callArgTexts, paramDefaults, callArgs);
475
+ if (semanticOp) {
476
+ halOps.push(semanticOp);
477
+ returnValue = "__hal_op_return__";
478
+ continue;
479
+ }
480
+ }
481
+ // Try to resolve semantic calls within compound expressions (e.g., gpioRead(pin) === HIGH)
482
+ const compoundResult = tryResolveCompoundSemanticReturn(retExpr, instance, paramNames, callArgTexts, paramDefaults, halOps, callArgs);
483
+ if (compoundResult !== null) {
484
+ returnValue = compoundResult;
485
+ continue;
486
+ }
487
+ // Handle: return new TypedArray(count) — mark as C array allocation
488
+ if (ts.isNewExpression(retExpr) && ts.isIdentifier(retExpr.expression)) {
489
+ const ctorName = retExpr.expression.text;
490
+ const elementType = TYPED_ARRAY_ELEMENT_MAP?.[ctorName];
491
+ if (elementType) {
492
+ const sizeArg = retExpr.arguments?.[0];
493
+ if (sizeArg) {
494
+ const size = resolveExpressionText(sizeArg, instance, paramNames, callArgTexts, paramDefaults);
495
+ if (size !== null) {
496
+ returnValue = `__TYPED_ARRAY__:${elementType}:${size}`;
497
+ continue;
498
+ }
499
+ }
500
+ }
501
+ }
502
+ // Fall back to expression text resolution
503
+ const resolved = resolveExpressionText(retExpr, instance, paramNames, callArgTexts, paramDefaults);
504
+ if (resolved !== null) {
505
+ returnValue = resolved.replace(/===/g, "==").replace(/!==/g, "!=");
506
+ }
507
+ }
508
+ }
509
+ // Demo #33 Finding D — value-returning HAL methods encode their C++ return
510
+ // as a rawCpp emitLine/halOp of the form `return <expr>;` (e.g.
511
+ // Preferences.getInt → `return Preferences.getInt(k, d);`), while the TS
512
+ // method body's own `return 0;` / `return "";` is just a type-checking
513
+ // fallback. Without this, var-init context (`const v = Preferences.getInt(...)`)
514
+ // captured the TS fallback (0) instead of the real C++ value, so the
515
+ // roundtrip returned the default. When the trailing halOp is a `return X;`
516
+ // raw op, hoist X into returnValue so both statement and expression/var-init
517
+ // contexts use the real C++ expression.
518
+ if (halOps.length > 0) {
519
+ const lastOp = halOps[halOps.length - 1];
520
+ if (lastOp.operation === "raw" && typeof lastOp.code === "string") {
521
+ const m = lastOp.code.match(/^\s*return\s+([\s\S]+?);\s*$/);
522
+ if (m) {
523
+ returnValue = m[1].trim();
524
+ // Drop the now-redundant raw op so it isn't emitted as a stray
525
+ // statement alongside the value capture.
526
+ halOps.pop();
527
+ emitLines.length = 0;
528
+ }
529
+ }
530
+ }
531
+ // Auto-passthrough for stub methods: if no emit() calls and the return is a literal
532
+ // (e.g., return 0), construct the C++ expression as <objectName>.<method>(<args>).
533
+ // Skip for Pin classes since Pin methods are typically lowered to standalone C calls (digitalRead/Write).
534
+ if (emitLines.length === 0 && isLiteralReturnValue(returnValue)
535
+ && instance.className !== "Pin"
536
+ && instance.className !== "OutputPin"
537
+ && instance.className !== "InputPin") {
538
+ const cppObj = resolveCppObjectName(instance);
539
+ if (cppObj) {
540
+ const argsStr = callArgTexts.join(", ");
541
+ return { emitLines: [], halOps: [], returnValue: `${cppObj}.${methodName}(${argsStr})` };
542
+ }
543
+ }
544
+ // Return null if nothing useful was resolved, allowing inline fallbacks to kick in
545
+ if (emitLines.length === 0 && halOps.length === 0 && returnValue === undefined) {
546
+ // take()/release() are intentional no-ops (ownership is compile-time only).
547
+ if (methodName === "take" || methodName === "release") {
548
+ return { emitLines: [], halOps: [] };
549
+ }
550
+ return null;
551
+ }
552
+ // Extract return type annotation to support type-narrowed pattern
553
+ // (e.g., Pin.asOutput(): OutputPin → returnClassName = "OutputPin")
554
+ let returnClassName;
555
+ const returnType = methodEntry.methodNode.type;
556
+ if (returnType && ts.isTypeReferenceNode(returnType) && ts.isIdentifier(returnType.typeName)) {
557
+ const name = returnType.typeName.text;
558
+ if (halClassRegistry.has(name) && name !== instance.className) {
559
+ returnClassName = name;
560
+ }
561
+ }
562
+ return { emitLines, halOps, returnValue, returnClassName };
563
+ }
564
+ /** Check if a resolved C++ value string is a simple literal (for stub detection). */
565
+ export function isLiteralReturnValue(val) {
566
+ if (val === undefined)
567
+ return false;
568
+ return val === "0" || val === "true" || val === "false" || val === "''" || val === '""';
569
+ }
570
+ /** Resolve the C++ object name from a HAL instance's constructor field values. */
571
+ export function resolveCppObjectName(instance) {
572
+ const fieldMap = halClassRegistry.get(instance.className)?.ctorFieldMap;
573
+ if (!fieldMap)
574
+ return null;
575
+ // Get the first field value from the instance that matches a constructor field
576
+ for (const [fieldName] of fieldMap) {
577
+ const val = instance.fieldValues.get(fieldName);
578
+ if (val)
579
+ return val;
580
+ }
581
+ return null;
582
+ }
583
+ /** Process a list of statements for emit/include/semantic calls/return. */
584
+ export function processStatementList(stmts, instance, paramNames, callArgTexts, emitLines, halOps, callArgs, paramDefaults, returnExpr) {
585
+ for (const stmt of stmts) {
586
+ if (ts.isExpressionStatement(stmt) && ts.isCallExpression(stmt.expression)) {
587
+ const call = stmt.expression;
588
+ if (ts.isIdentifier(call.expression)) {
589
+ if (call.expression.text === "include") {
590
+ const firstArg = call.arguments[0];
591
+ if (firstArg && ts.isStringLiteral(firstArg)) {
592
+ requiredIncludes.add(firstArg.text);
593
+ }
594
+ }
595
+ else {
596
+ // R3: Try semantic HAL function call
597
+ for (const arg of call.arguments) {
598
+ extractAndRegisterCallbacks(arg, paramNames, callArgs, callArgTexts);
599
+ }
600
+ const semanticOp = tryResolveSemanticCall(call.expression.text, call.arguments, instance, paramNames, callArgTexts, paramDefaults, callArgs);
601
+ if (semanticOp) {
602
+ halOps.push(semanticOp);
603
+ }
604
+ }
605
+ }
606
+ }
607
+ }
608
+ }
609
+ /** Reset resolver state (called between builds). */
610
+ export function resetHALResolver() {
611
+ halInstances.clear();
612
+ floatVariables.clear();
613
+ getContext().callbackPlaceholderCounter = 0;
614
+ getContext().snprintfCounter = 0;
615
+ }
616
+ /** Resolve a hal-expr IR node to its C++ text using the active strategy. */
617
+ export function setActiveStrategy(strategy) {
618
+ getContext().activeStrategy = strategy;
619
+ }
620
+ export function resolveHALExprToText(expr) {
621
+ const strategy = getContext().activeStrategy;
622
+ if (!strategy?.resolveHALOperation)
623
+ return null;
624
+ const resolved = strategy.resolveHALOperation(expr.operation);
625
+ if (resolved?.expression)
626
+ return resolved.expression;
627
+ if (resolved?.code)
628
+ return resolved.code.replace(/;\s*$/, "");
629
+ return null;
630
+ }
631
+ export function registerFloatVariable(name) {
632
+ floatVariables.add(name);
633
+ }
634
+ /** Build snprintf prelude lines from a string_concat expression.
635
+ * Returns { lines, bufferName } or null if the expression can't be formatted. */
636
+ export function buildSnprintfFromConcat(expr) {
637
+ let formatString = "";
638
+ const args = [];
639
+ let estimatedLength = 1;
640
+ const prelude = [];
641
+ requiredIncludes.add("<stdio.h>");
642
+ for (const part of expr.parts) {
643
+ const text = renderExprAsText(part);
644
+ if (part.kind === "string") {
645
+ formatString += text.slice(1, -1).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
646
+ estimatedLength += part.value.length;
647
+ }
648
+ else if (part.kind === "number") {
649
+ const isFloat = part.cppType === "float" || part.cppType === "double" || !Number.isInteger(part.value);
650
+ if (isFloat) {
651
+ requiredIncludes.add("<stdlib.h>");
652
+ const floatBuf = `__cuttlefish_float_${getContext().snprintfCounter++}`;
653
+ prelude.push(`char ${floatBuf}[16];`);
654
+ prelude.push(`dtostrf(${text}, 0, 1, ${floatBuf});`);
655
+ formatString += "%s";
656
+ args.push(floatBuf);
657
+ estimatedLength += 16;
658
+ }
659
+ else {
660
+ formatString += "%d";
661
+ args.push(text);
662
+ estimatedLength += 12;
663
+ }
664
+ }
665
+ else {
666
+ // Check for string variable reference: template_string wrapping an identifier
667
+ const isStringVar = part.kind === "template_string"
668
+ && part.expression.kind === "identifier"
669
+ && (activeStringVars.has(part.expression.value) || getCurrentIrTypeScope()?.locals.get(part.expression.value) === "std::string" || getCurrentIrTypeScope()?.globals.get(part.expression.value) === "std::string");
670
+ // Check for float variable reference: template_string wrapping an identifier
671
+ const isFloatVar = part.kind === "template_string"
672
+ && part.expression.kind === "identifier"
673
+ && floatVariables.has(part.expression.value);
674
+ if (isStringVar) {
675
+ formatString += "%s";
676
+ const varName = part.expression.value;
677
+ const varType = getCurrentIrTypeScope()?.locals.get(varName) || getCurrentIrTypeScope()?.globals.get(varName) || "";
678
+ const cleanType = varType.replace(/\bconst\b\s*/g, "").trim();
679
+ if (cleanType && cleanType !== "char*" && cleanType !== "const char*") {
680
+ args.push(`${text}.c_str()`);
681
+ }
682
+ else {
683
+ args.push(text);
684
+ }
685
+ estimatedLength += 32;
686
+ }
687
+ else if (isFloatVar) {
688
+ requiredIncludes.add("<stdlib.h>");
689
+ const floatBuf = `__cuttlefish_float_${getContext().snprintfCounter++}`;
690
+ prelude.push(`char ${floatBuf}[16];`);
691
+ prelude.push(`dtostrf(${text}, 0, 1, ${floatBuf});`);
692
+ formatString += "%s";
693
+ args.push(floatBuf);
694
+ estimatedLength += 16;
695
+ }
696
+ else if (part.kind === "template_string" && part.expression.kind === "hal-expr") {
697
+ // HAL expression inside template literal — resolve via strategy
698
+ const resolved = resolveHALExprToText(part.expression);
699
+ if (resolved !== null) {
700
+ formatString += "%d";
701
+ args.push(resolved);
702
+ estimatedLength += 12;
703
+ }
704
+ else {
705
+ formatString += "%d";
706
+ args.push(text);
707
+ estimatedLength += 12;
708
+ }
709
+ }
710
+ else {
711
+ const numVal = Number(text);
712
+ if (!isNaN(numVal) && !Number.isInteger(numVal)) {
713
+ requiredIncludes.add("<stdlib.h>");
714
+ const floatBuf = `__cuttlefish_float_${getContext().snprintfCounter++}`;
715
+ const precision = text.includes(".") ? text.split(".")[1].length : 1;
716
+ prelude.push(`char ${floatBuf}[16];`);
717
+ prelude.push(`dtostrf(${text}, 0, ${precision}, ${floatBuf});`);
718
+ formatString += "%s";
719
+ args.push(floatBuf);
720
+ estimatedLength += 16;
721
+ }
722
+ else {
723
+ formatString += "%d";
724
+ args.push(text);
725
+ estimatedLength += 12;
726
+ }
727
+ }
728
+ }
729
+ }
730
+ const bufName = `__cuttlefish_snprintf_${getContext().snprintfCounter++}`;
731
+ const bufSize = Math.max(estimatedLength + 1, 16);
732
+ prelude.push(`char ${bufName}[${bufSize}];`);
733
+ prelude.push(`snprintf(${bufName}, sizeof(${bufName}), "${formatString}", ${args.join(", ")});`);
734
+ return { lines: prelude, bufferName: bufName };
735
+ }