@typecad/cuttlefish 1.0.0-alpha.13 → 1.0.0-alpha.15

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 (343) hide show
  1. package/README.md +82 -79
  2. package/assets/editor-extensions/typecad-debug/README.md +141 -0
  3. package/assets/editor-extensions/typecad-debug/out/extension.js +347 -0
  4. package/assets/editor-extensions/typecad-debug/out/extension.js.map +1 -0
  5. package/assets/editor-extensions/typecad-debug/package.json +76 -0
  6. package/assets/editor-extensions/typecad-ui/LICENSE +27 -0
  7. package/assets/editor-extensions/typecad-ui/README.md +52 -0
  8. package/assets/editor-extensions/typecad-ui/file-icon-dark.png +0 -0
  9. package/assets/editor-extensions/typecad-ui/file-icon-light.png +0 -0
  10. package/assets/editor-extensions/typecad-ui/icon.png +0 -0
  11. package/assets/editor-extensions/typecad-ui/language-configuration.json +43 -0
  12. package/assets/editor-extensions/typecad-ui/package.json +54 -0
  13. package/assets/editor-extensions/typecad-ui/snippets/typecad-ui.json +80 -0
  14. package/assets/editor-extensions/typecad-ui/syntaxes/markdown-ui.json +45 -0
  15. package/assets/editor-extensions/typecad-ui/syntaxes/typecad-ui.tmLanguage.json +1269 -0
  16. package/dist/api/config.d.ts +30 -27
  17. package/dist/api/schema/types.d.ts +20 -3
  18. package/dist/api/shared/async-runtime-static.d.ts +1 -1
  19. package/dist/api/shared/async-runtime-static.js +5 -5
  20. package/dist/api/shared/async-types.d.ts +3 -2
  21. package/dist/api/shared/board-catalog.d.ts +2 -0
  22. package/dist/api/shared/board-catalog.js +6 -0
  23. package/dist/api/shared/board-resolver.d.ts +4 -7
  24. package/dist/api/shared/board-resolver.js +2 -1
  25. package/dist/api/shared/coop-scheduler.d.ts +1 -1
  26. package/dist/api/shared/coop-scheduler.js +1 -1
  27. package/dist/api/shared/cpp-type-ir.d.ts +1 -1
  28. package/dist/api/shared/cpp-type-ir.js +1 -1
  29. package/dist/api/shared/display-profile.d.ts +1 -1
  30. package/dist/api/shared/display-profile.js +3 -5
  31. package/dist/api/shared/framework-manifest-registry.d.ts +1 -1
  32. package/dist/api/shared/framework-manifest-registry.js +0 -1
  33. package/dist/api/shared/framework-manifest.d.ts +65 -507
  34. package/dist/api/shared/framework-manifest.js +6 -11
  35. package/dist/api/shared/hal-op-ir.d.ts +294 -685
  36. package/dist/api/shared/hal-op-ir.js +31 -65
  37. package/dist/api/shared/index.d.ts +0 -4
  38. package/dist/api/shared/index.js +1 -7
  39. package/dist/api/shared/ir-core.d.ts +6 -0
  40. package/dist/api/shared/ir-declarations.d.ts +4 -0
  41. package/dist/api/shared/ir.d.ts +1 -3
  42. package/dist/api/shared/platform-strategy.d.ts +14 -37
  43. package/dist/api/shared/polyfill-helper-registry.js +0 -5
  44. package/dist/api/shared/polyfill-types.js +0 -27
  45. package/dist/api/shared/promise-runtime.js +3 -3
  46. package/dist/api/shared/spdx-licenses.d.ts +12 -10
  47. package/dist/api/shared/spdx-licenses.js +1 -1
  48. package/dist/api/shared/toolchain-types.d.ts +1 -1
  49. package/dist/api/shared/toolchain-types.js +3 -3
  50. package/dist/api/shared/types.d.ts +0 -3
  51. package/dist/api/shared/validate-framework-manifest.js +13 -33
  52. package/dist/board-catalog/dts-reader.d.ts +220 -0
  53. package/dist/board-catalog/dts-reader.js +1052 -0
  54. package/dist/board-catalog/index.d.ts +11 -0
  55. package/dist/board-catalog/index.js +12 -0
  56. package/dist/board-catalog/pinconfig.d.ts +48 -0
  57. package/dist/board-catalog/pinconfig.js +263 -0
  58. package/dist/board-catalog/sdk.d.ts +82 -0
  59. package/dist/board-catalog/sdk.js +180 -0
  60. package/dist/board-catalog/store.d.ts +196 -0
  61. package/dist/board-catalog/store.js +363 -0
  62. package/dist/board-catalog/types.d.ts +233 -0
  63. package/dist/board-catalog/types.js +7 -0
  64. package/dist/board-catalog/walker.d.ts +112 -0
  65. package/dist/board-catalog/walker.js +1101 -0
  66. package/dist/cli-utils.d.ts +2 -2
  67. package/dist/cli-utils.js +5 -5
  68. package/dist/cli.js +227 -244
  69. package/dist/config-loader.d.ts +14 -19
  70. package/dist/config-loader.js +242 -78
  71. package/dist/config-schema.d.ts +19 -227
  72. package/dist/config-schema.js +9 -8
  73. package/dist/contract/board-generator.d.ts +4 -4
  74. package/dist/contract/board-generator.js +16 -5
  75. package/dist/contract/contract-parser.d.ts +36 -127
  76. package/dist/contract/contract-parser.js +76 -0
  77. package/dist/contract/index.d.ts +1 -1
  78. package/dist/contract/index.js +70 -21
  79. package/dist/create/active-board-catalog.d.ts +9 -0
  80. package/dist/create/active-board-catalog.js +94 -0
  81. package/dist/create/board-catalog.generated.d.ts +96 -0
  82. package/dist/create/board-catalog.generated.js +128737 -0
  83. package/dist/create/board-search.d.ts +23 -0
  84. package/dist/create/board-search.js +88 -0
  85. package/dist/create/editor-integration.d.ts +32 -0
  86. package/dist/create/editor-integration.js +257 -0
  87. package/dist/create/eslint-rules-template.d.ts +2 -2
  88. package/dist/create/framework-catalog.d.ts +48 -8
  89. package/dist/create/framework-catalog.js +58 -44
  90. package/dist/create/index.d.ts +8 -13
  91. package/dist/create/index.js +5 -9
  92. package/dist/create/mcu-target.d.ts +39 -0
  93. package/dist/create/mcu-target.js +80 -0
  94. package/dist/create/pack-targets.d.ts +19 -0
  95. package/dist/create/pack-targets.js +56 -0
  96. package/dist/create/{init-scaffold.d.ts → scaffold.d.ts} +8 -11
  97. package/dist/create/{init-scaffold.js → scaffold.js} +28 -87
  98. package/dist/create/templates.d.ts +51 -0
  99. package/dist/create/{init-templates.js → templates.js} +135 -116
  100. package/dist/create/wizard.d.ts +19 -0
  101. package/dist/create/wizard.js +314 -0
  102. package/dist/debug/preprocessor.d.ts +5 -0
  103. package/dist/debug/preprocessor.js +31 -7
  104. package/dist/diagnostics/diagnostics-report.d.ts +1 -1
  105. package/dist/diagnostics/diagnostics-report.js +3 -11
  106. package/dist/diagnostics/json-schema.d.ts +1 -1
  107. package/dist/diagnostics/md-writer.js +2 -2
  108. package/dist/diagnostics/mermaid-builder.d.ts +1 -1
  109. package/dist/diagnostics/mermaid-builder.js +1 -1
  110. package/dist/emit/cpp-emitter.js +4 -1
  111. package/dist/emit/emitters/class-emitter.js +32 -4
  112. package/dist/emit/emitters/emitter-context.d.ts +11 -0
  113. package/dist/emit/emitters/entrypoint-synthesizer.js +1 -1
  114. package/dist/emit/emitters/function-emitter-impl.js +33 -17
  115. package/dist/emit/emitters/output-finalizer.d.ts +7 -0
  116. package/dist/emit/emitters/output-finalizer.js +31 -23
  117. package/dist/emit/emitters/setup.js +147 -63
  118. package/dist/emit/emitters/top-level-prep.js +1 -1
  119. package/dist/emit/emitters/type-decl-emitter.js +13 -8
  120. package/dist/emit/emitters/ui-emitter.js +1 -1
  121. package/dist/emit/expression-renderer.d.ts +3 -3
  122. package/dist/emit/expression-renderer.js +17 -7
  123. package/dist/emit/route-hal-op.js +17 -48
  124. package/dist/emit/snprintf-helpers.d.ts +1 -3
  125. package/dist/emit/snprintf-helpers.js +1 -39
  126. package/dist/emit/statement-renderer.d.ts +0 -4
  127. package/dist/emit/statement-renderer.js +54 -45
  128. package/dist/emit/utils/async-state-machine.d.ts +16 -9
  129. package/dist/emit/utils/async-state-machine.js +56 -54
  130. package/dist/emit/utils/comment-helpers.d.ts +0 -8
  131. package/dist/emit/utils/comment-helpers.js +1 -1
  132. package/dist/emit/utils/cpp-helpers.d.ts +2 -2
  133. package/dist/emit/utils/cpp-helpers.js +2 -2
  134. package/dist/emit/utils/hal-op-cpp-type.js +4 -6
  135. package/dist/emit/utils/index.d.ts +3 -3
  136. package/dist/emit/utils/index.js +3 -3
  137. package/dist/emit/utils/type-inference.d.ts +1 -29
  138. package/dist/emit/utils/type-inference.js +0 -75
  139. package/dist/framework-package.d.ts +3 -0
  140. package/dist/framework-package.js +23 -6
  141. package/dist/framework-registry.d.ts +5 -5
  142. package/dist/frameworks/native/graphics/terminal-preview.d.ts +5 -0
  143. package/dist/frameworks/native/graphics/terminal-preview.js +33 -0
  144. package/dist/frameworks/native/index.d.ts +3 -0
  145. package/dist/frameworks/native/index.js +15 -0
  146. package/dist/frameworks/native/native-compile.d.ts +17 -0
  147. package/dist/frameworks/native/native-compile.js +170 -0
  148. package/dist/frameworks/native/native-config.d.ts +41 -0
  149. package/dist/frameworks/native/native-config.js +7 -0
  150. package/dist/frameworks/native/strategy.d.ts +97 -0
  151. package/dist/frameworks/native/strategy.js +664 -0
  152. package/dist/ir/board-resolver.d.ts +18 -23
  153. package/dist/ir/board-resolver.js +43 -401
  154. package/dist/ir/build-ir-state.d.ts +3 -0
  155. package/dist/ir/build-ir-state.js +42 -0
  156. package/dist/ir/build-ir.d.ts +1 -1
  157. package/dist/ir/build-ir.js +64 -45
  158. package/dist/ir/declaration-builders.js +1 -0
  159. package/dist/ir/expression-to-ir.js +17 -10
  160. package/dist/ir/hal/hal-emitter.d.ts +0 -2
  161. package/dist/ir/hal/hal-emitter.js +92 -41
  162. package/dist/ir/hal/hal-parser.d.ts +5 -5
  163. package/dist/ir/hal/hal-parser.js +128 -36
  164. package/dist/ir/hal/hal-plugins.d.ts +0 -1
  165. package/dist/ir/hal/hal-plugins.js +425 -716
  166. package/dist/ir/heap-analysis.js +2 -41
  167. package/dist/ir/identifier-collector.js +32 -20
  168. package/dist/ir/network-validation.js +7 -11
  169. package/dist/ir/ownership-analysis.js +75 -13
  170. package/dist/ir/peripheral-usage.d.ts +8 -2
  171. package/dist/ir/peripheral-usage.js +78 -149
  172. package/dist/ir/peripheral-validation.js +23 -3
  173. package/dist/ir/pin-capability-validation.d.ts +12 -0
  174. package/dist/ir/pin-capability-validation.js +61 -97
  175. package/dist/ir/pin-mode-validation.js +5 -5
  176. package/dist/ir/program-analysis.d.ts +8 -15
  177. package/dist/ir/program-analysis.js +140 -323
  178. package/dist/ir/pulldown-validation.js +11 -4
  179. package/dist/ir/resource-analysis.js +15 -3
  180. package/dist/ir/statement-to-ir.js +0 -6
  181. package/dist/ir/timing-validation.js +1 -1
  182. package/dist/ir/transformers/array-methods.js +11 -5
  183. package/dist/ir/transformers/call-statement.js +20 -87
  184. package/dist/ir/transformers/callback-context-registry.d.ts +1 -1
  185. package/dist/ir/transformers/callback-context-registry.js +8 -16
  186. package/dist/ir/transformers/control-flow.js +0 -13
  187. package/dist/ir/transformers/expressions.js +10 -9
  188. package/dist/ir/transformers/hal-call-resolver.d.ts +2 -2
  189. package/dist/ir/transformers/hal-call-resolver.js +24 -10
  190. package/dist/ir/transformers/hal-emit-helpers.d.ts +2 -2
  191. package/dist/ir/transformers/hal-emit-helpers.js +2 -2
  192. package/dist/ir/transformers/namespace-methods.js +1 -1
  193. package/dist/ir/transformers/ui-callback-lowering.d.ts +0 -2
  194. package/dist/ir/transformers/ui-callback-lowering.js +0 -24
  195. package/dist/ir/transformers/variables.js +338 -42
  196. package/dist/ir/try-catch-validation.js +4 -3
  197. package/dist/ir/type-resolution.js +15 -4
  198. package/dist/ir/validation-orchestrator.js +0 -9
  199. package/dist/libdef/cpp-to-decl.d.ts +0 -9
  200. package/dist/libdef/cpp-to-decl.js +0 -72
  201. package/dist/libdef/registry.d.ts +0 -1
  202. package/dist/libdef/registry.js +1 -24
  203. package/dist/library/catalog.d.ts +35 -0
  204. package/dist/library/catalog.js +67 -0
  205. package/dist/library/cli.d.ts +2 -0
  206. package/dist/library/cli.js +168 -0
  207. package/dist/library/init.d.ts +25 -0
  208. package/dist/library/init.js +397 -0
  209. package/dist/library/install.d.ts +9 -0
  210. package/dist/library/install.js +80 -0
  211. package/dist/library/registry-search.d.ts +34 -0
  212. package/dist/library/registry-search.js +50 -0
  213. package/dist/library/validate.d.ts +17 -0
  214. package/dist/library/validate.js +175 -0
  215. package/dist/library-packages.d.ts +95 -0
  216. package/dist/library-packages.js +275 -0
  217. package/dist/mapping/peripheral-names.js +9 -4
  218. package/dist/mapping/source-map.d.ts +3 -4
  219. package/dist/mapping/source-map.js +12 -18
  220. package/dist/orchestrator/graph-builder.d.ts +3 -3
  221. package/dist/orchestrator/graph-builder.js +29 -12
  222. package/dist/orchestrator/type-checker.d.ts +3 -3
  223. package/dist/orchestrator/type-checker.js +3 -3
  224. package/dist/platform/async-runtime.js +1 -1
  225. package/dist/platform/coop-scheduler-runtime.js +2 -2
  226. package/dist/platform/generic-debug-codegen.d.ts +36 -0
  227. package/dist/platform/generic-debug-codegen.js +185 -0
  228. package/dist/platform/generic-strategy.d.ts +3 -4
  229. package/dist/platform/generic-strategy.js +5 -80
  230. package/dist/platform/index.js +1 -1
  231. package/dist/platform/registry.d.ts +2 -2
  232. package/dist/platform/registry.js +2 -2
  233. package/dist/preview/client.js +17 -3
  234. package/dist/safety/asil-decorators.d.ts +32 -0
  235. package/dist/safety/asil-decorators.js +32 -0
  236. package/dist/safety/authoring.d.ts +85 -0
  237. package/dist/safety/authoring.js +61 -0
  238. package/dist/safety/engine.d.ts +3 -0
  239. package/dist/safety/engine.js +83 -0
  240. package/dist/safety/hal/ops.d.ts +48 -0
  241. package/dist/safety/hal/ops.js +46 -0
  242. package/dist/safety/iso26262/analyze.d.ts +13 -0
  243. package/dist/safety/iso26262/analyze.js +29 -0
  244. package/dist/safety/iso26262/asil.d.ts +10 -0
  245. package/dist/safety/iso26262/asil.js +22 -0
  246. package/dist/safety/iso26262/collect.d.ts +14 -0
  247. package/dist/safety/iso26262/collect.js +137 -0
  248. package/dist/safety/iso26262/goto-checker.d.ts +10 -0
  249. package/dist/safety/iso26262/goto-checker.js +72 -0
  250. package/dist/safety/iso26262/heap-checker.d.ts +7 -0
  251. package/dist/safety/iso26262/heap-checker.js +57 -0
  252. package/dist/safety/iso26262/init-checker.d.ts +13 -0
  253. package/dist/safety/iso26262/init-checker.js +127 -0
  254. package/dist/safety/iso26262/loop-checker.d.ts +7 -0
  255. package/dist/safety/iso26262/loop-checker.js +86 -0
  256. package/dist/safety/iso26262/recursion-checker.d.ts +9 -0
  257. package/dist/safety/iso26262/recursion-checker.js +134 -0
  258. package/dist/safety/iso26262/sidecar-writer.d.ts +25 -0
  259. package/dist/safety/iso26262/sidecar-writer.js +13 -0
  260. package/dist/safety/passes/pinMode-intercept.d.ts +3 -0
  261. package/dist/safety/passes/pinMode-intercept.js +60 -0
  262. package/dist/safety/runtime/mode-table.d.ts +5 -0
  263. package/dist/safety/runtime/mode-table.js +55 -0
  264. package/dist/safety/runtime/polyfills.d.ts +6 -0
  265. package/dist/safety/runtime/polyfills.js +12 -0
  266. package/dist/safety/runtime/result-struct.d.ts +11 -0
  267. package/dist/safety/runtime/result-struct.js +98 -0
  268. package/dist/safety/runtime/safe-int.d.ts +33 -0
  269. package/dist/safety/runtime/safe-int.js +170 -0
  270. package/dist/safety/runtime/safe-traits.d.ts +4 -0
  271. package/dist/safety/runtime/safe-traits.js +95 -0
  272. package/dist/safety/runtime/safe-variable.d.ts +39 -0
  273. package/dist/safety/runtime/safe-variable.js +226 -0
  274. package/dist/safety/runtime/vote.d.ts +8 -0
  275. package/dist/safety/runtime/vote.js +92 -0
  276. package/dist/safety/runtime/write-verify.d.ts +8 -0
  277. package/dist/safety/runtime/write-verify.js +71 -0
  278. package/dist/safety/safe-int-types.d.ts +45 -0
  279. package/dist/safety/safe-int-types.js +25 -0
  280. package/dist/safety/safe-variable-types.d.ts +29 -0
  281. package/dist/safety/safe-variable-types.js +19 -0
  282. package/dist/safety/safety-bridge.d.ts +5 -6
  283. package/dist/safety/safety-bridge.js +15 -28
  284. package/dist/safety/sidecar-bridge.d.ts +1 -3
  285. package/dist/safety/sidecar-bridge.js +3 -20
  286. package/dist/safety/specifiers.d.ts +6 -0
  287. package/dist/safety/specifiers.js +14 -0
  288. package/dist/safety-hook.d.ts +8 -7
  289. package/dist/safety-hook.js +12 -13
  290. package/dist/testing.d.ts +5 -6
  291. package/dist/testing.js +5 -5
  292. package/dist/transpile/resolution.d.ts +1 -1
  293. package/dist/transpile/resolution.js +18 -9
  294. package/dist/transpile/test-pins.d.ts +18 -0
  295. package/dist/transpile/test-pins.js +142 -0
  296. package/dist/transpile.d.ts +1 -2
  297. package/dist/transpile.js +90 -29
  298. package/dist/types.d.ts +45 -26
  299. package/dist/ui-hook.d.ts +1 -1
  300. package/dist/utils/cli.d.ts +2 -2
  301. package/dist/utils/cli.js +160 -157
  302. package/dist/utils/fs.d.ts +3 -4
  303. package/dist/utils/fs.js +4 -5
  304. package/dist/utils/strings.d.ts +0 -1
  305. package/dist/utils/strings.js +0 -8
  306. package/dist/utils/ui.d.ts +5 -2
  307. package/dist/utils/ui.js +13 -3
  308. package/package.json +158 -149
  309. package/dist/add-preset.d.ts +0 -4
  310. package/dist/add-preset.js +0 -74
  311. package/dist/api/shared/async-symbol-detector.d.ts +0 -11
  312. package/dist/api/shared/async-symbol-detector.js +0 -140
  313. package/dist/api/shared/native-display-op-resolver.d.ts +0 -10
  314. package/dist/api/shared/native-display-op-resolver.js +0 -64
  315. package/dist/create/board-checklist.d.ts +0 -2
  316. package/dist/create/board-checklist.js +0 -52
  317. package/dist/create/board-codegen.d.ts +0 -9
  318. package/dist/create/board-codegen.js +0 -238
  319. package/dist/create/board-generators.d.ts +0 -12
  320. package/dist/create/board-generators.js +0 -651
  321. package/dist/create/board-spec.d.ts +0 -637
  322. package/dist/create/board-spec.js +0 -214
  323. package/dist/create/init-templates.d.ts +0 -28
  324. package/dist/create/init-wizard.d.ts +0 -8
  325. package/dist/create/init-wizard.js +0 -182
  326. package/dist/install/framework-catalog.d.ts +0 -53
  327. package/dist/install/framework-catalog.js +0 -107
  328. package/dist/install/handle-install.d.ts +0 -35
  329. package/dist/install/handle-install.js +0 -177
  330. package/dist/install/index.d.ts +0 -4
  331. package/dist/install/index.js +0 -3
  332. package/dist/ir/pin-state-tracking.d.ts +0 -58
  333. package/dist/ir/pin-state-tracking.js +0 -182
  334. package/dist/ir/pwm-timer-sharing.d.ts +0 -4
  335. package/dist/ir/pwm-timer-sharing.js +0 -94
  336. package/dist/ir/timer0-pwm-timing-conflict.d.ts +0 -4
  337. package/dist/ir/timer0-pwm-timing-conflict.js +0 -72
  338. package/dist/libdef/component-decls.d.ts +0 -2
  339. package/dist/libdef/component-decls.js +0 -6
  340. package/dist/libdef/component-discovery.d.ts +0 -43
  341. package/dist/libdef/component-discovery.js +0 -83
  342. package/dist/theme-tokens.d.ts +0 -22
  343. package/dist/theme-tokens.js +0 -172
@@ -8,7 +8,6 @@
8
8
  // UI-specific behaviour that still happens here:
9
9
  // - signal.set(value) → assign (already in callToStatement)
10
10
  // - signal() → identifier (already in expressionToIR)
11
- // - console.* → platform transform (already at emit via StatementRenderer)
12
11
  // - CSS color string literals → rgb int (resolveColorIR walk on the IR)
13
12
  // - canvas ctx.* → ui_display_* (ambient canvas ctx + rewriteCanvasCall)
14
13
  // ---------------------------------------------------------------------------
@@ -246,15 +245,7 @@ export function renameIdentifiersInStatements(statements, from, to) {
246
245
  return statements.map(renameStmt);
247
246
  }
248
247
  // ── Console-in-callback tracking (legacy string-bake leftover) ─────────────
249
- let _loweredConsoleInCallback = false;
250
- export function noteConsoleInCallback() {
251
- _loweredConsoleInCallback = true;
252
- }
253
- export function loweredConsoleInCallback() {
254
- return _loweredConsoleInCallback;
255
- }
256
248
  export function resetCallbackLoweringState() {
257
- _loweredConsoleInCallback = false;
258
249
  endCanvasAmbient();
259
250
  }
260
251
  // ── Ambient canvas context ─────────────────────────────────────────────────
@@ -304,21 +295,6 @@ function callbackTypeMaps() {
304
295
  * go through lowerStatementList instead.
305
296
  */
306
297
  export function lowerCallbackExpr(expr, sourceText, diagnostics) {
307
- if (ts.isCallExpression(expr) &&
308
- ts.isPropertyAccessExpression(expr.expression) &&
309
- ts.isIdentifier(expr.expression.expression) &&
310
- expr.expression.expression.text === "console") {
311
- const method = expr.expression.name.text;
312
- const renderedArgs = expr.arguments
313
- .map((arg) => renderExprAsText(expressionToIR(arg, sourceText, diagnostics)))
314
- .join(" << ");
315
- _loweredConsoleInCallback = true;
316
- const strategy = getContext().activeStrategy;
317
- if (strategy) {
318
- return strategy.transformConsoleCall(method, renderedArgs, false).replace(/;$/, "");
319
- }
320
- return `std::cout << ${renderedArgs} << std::endl`;
321
- }
322
298
  if (ts.isCallExpression(expr) &&
323
299
  ts.isPropertyAccessExpression(expr.expression) &&
324
300
  expr.expression.name.text === "set" &&
@@ -8,18 +8,13 @@ import { renderExprAsText } from "../render-expr.js";
8
8
  import { expressionToIR } from "../expression-to-ir.js";
9
9
  import { buildInlineForLoop } from "./array-methods.js";
10
10
  import { isKnownHALClass, getCtorIncludes, getHALCtorFieldMap, registerFloatVariable, resolveHALReceiver, isHALSingleton, } from "../hal-resolver.js";
11
- import { httpFactoryVerb, httpUrlArgText } from "../hal/hal-parser.js";
11
+ import { requestCtorFields, halClassRegistry } from "../hal/hal-parser.js";
12
12
  import { resolveHALCallForVarInit } from "./hal-call-resolver.js";
13
13
  import { recordSignal } from "./ui-call-resolver.js";
14
14
  import { hasSafetyHook, requireSafetyHook } from "../../safety-hook.js";
15
- function replaceHalReadBufferPlaceholder(op, varName) {
16
- if (op.operation === "i2c.read_buffer" && op.buffer === "__HAL_READ_BUF__") {
17
- return { ...op, buffer: varName };
18
- }
19
- if (op.operation === "spi.read_buffer" && op.buffer === "__HAL_READ_BUF__") {
20
- return { ...op, buffer: varName };
21
- }
22
- return op;
15
+ function replaceHalReadBufferPlaceholder(_op, _varName) {
16
+ // i2c/spi read_buffer ops were removed with the legacy Wire/SPI surfaces.
17
+ return _op;
23
18
  }
24
19
  export function assignmentOperatorToString(kind) {
25
20
  switch (kind) {
@@ -445,10 +440,8 @@ export function variableStatementToIR(statement, fileName, sourceText, diagnosti
445
440
  fieldValues.set("_bus", arg.text);
446
441
  else if (className === "SPIBus")
447
442
  fieldValues.set("_bus", arg.text);
448
- else if (className === "SerialPort")
443
+ else if (className === "UART")
449
444
  fieldValues.set("_port", arg.text);
450
- else if (className === "EEPROMClass")
451
- fieldValues.set("_name", arg.text);
452
445
  }
453
446
  else if (ts.isNumericLiteral(arg)) {
454
447
  if (className === "Pin")
@@ -460,15 +453,273 @@ export function variableStatementToIR(statement, fileName, sourceText, diagnosti
460
453
  }
461
454
  }
462
455
  }
463
- // new HttpRequest(method, url) — positional ctor fields. The URL is
464
- // stored as C++ expression text (quoted literal or identifier).
465
- if (className === "HttpRequest" && ctorArgs && ctorArgs.length >= 2) {
466
- const methodArg = ctorArgs[0];
467
- if (ts.isStringLiteral(methodArg))
468
- fieldValues.set("_method", methodArg.text.toUpperCase());
469
- const urlText = httpUrlArgText(ctorArgs[1]);
470
- if (urlText)
471
- fieldValues.set("_url", urlText);
456
+ // new Sensor(SENSOR.<part>, I2C1.device(0x44))the generic
457
+ // DT-bound peripheral. The part token is kept as text (property
458
+ // access text like 'SENSOR.sensirion_sht3xd'); the bus + address
459
+ // come from the I2CTarget the second arg resolves to. The lowering
460
+ // resolves the token against the generated catalog.
461
+ if (className === "Sensor" && ctorArgs && ctorArgs.length >= 2) {
462
+ const dev = resolveSensorDeviceArg(ctorArgs[1]);
463
+ if (dev) {
464
+ fieldValues.set("_part", ctorArgs[0].getText());
465
+ fieldValues.set("_bus", dev.bus);
466
+ fieldValues.set("_kind", dev.kind);
467
+ fieldValues.set("_port", dev.port);
468
+ }
469
+ else {
470
+ diagnostics.push(makeDiagnostic(sourceText, declaration.pos, `new Sensor(...) second argument must be an I2C device (e.g. I2C1.device(0x44), possibly via a const) — got '${ctorArgs[1].getText()}'.`, "error", "HAL_SENSOR_CTOR"));
471
+ }
472
+ // opts object literal: numeric properties pass through, pin
473
+ // identifiers resolve to their number (alert?: PA5). Absent
474
+ // properties fall back to the class-field initializer defaults
475
+ // (_spiHz = 1000000 etc.) so `this._spiHz` in an inlined method
476
+ // body always resolves.
477
+ const sensorDefaults = halClassRegistry.get("Sensor")?.fieldDefaults;
478
+ if (sensorDefaults) {
479
+ for (const [name, val] of sensorDefaults) {
480
+ if (!fieldValues.has(name))
481
+ fieldValues.set(name, val);
482
+ }
483
+ }
484
+ if (ctorArgs.length >= 3 && ts.isObjectLiteralExpression(ctorArgs[2])) {
485
+ for (const prop of ctorArgs[2].properties) {
486
+ if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name))
487
+ continue;
488
+ const name = prop.name.text;
489
+ if (ts.isNumericLiteral(prop.initializer)) {
490
+ fieldValues.set(`_${name}`, prop.initializer.text);
491
+ }
492
+ else if (ts.isIdentifier(prop.initializer)) {
493
+ const inst = halInstances.get(prop.initializer.text);
494
+ fieldValues.set(`_${name}`, inst?.fieldValues.get("_pin") ?? inst?.fieldValues.get("pin") ?? prop.initializer.text);
495
+ }
496
+ }
497
+ }
498
+ }
499
+ // new Request(method, url, opts?) — shared capture (also serves
500
+ // bare `new Request(...).send()` receivers via resolveHALReceiver).
501
+ if (className === "Request") {
502
+ const reqFields = requestCtorFields(ctorArgs, ctorArgs?.[0]?.getSourceFile());
503
+ if (reqFields)
504
+ for (const [k, v] of reqFields)
505
+ fieldValues.set(k, v);
506
+ }
507
+ // Thin Zephyr-shaped peripherals (GPIO/PWM/ADC/DAC/
508
+ // Watchdog/Counter — hal/gpio-pin.ts and siblings). Same discipline
509
+ // as Sensor: construction facts become instance fields. Pin args
510
+ // keep their identifier/text form (resolved to numbers later);
511
+ // flag/gain token EXPRESSIONS keep their source text — the lowering
512
+ // maps token names to C macros; numeric opts props pass through as
513
+ // numbers (numeric separators stripped for C++).
514
+ const thinPinFirst = { GPIO: true, PWM: true, ADC: true, DAC: true };
515
+ if (thinPinFirst[className] && ctorArgs && ctorArgs.length >= 1) {
516
+ const pinArg = ctorArgs[0];
517
+ if (ts.isIdentifier(pinArg)) {
518
+ // resolveHALReceiver seeds board-module pin constants (LED,
519
+ // BUTTON, datasheet names) into halInstances on first lookup —
520
+ // a plain halInstances.get() would miss them.
521
+ const inst = halInstances.get(pinArg.text) ?? resolveHALReceiver(pinArg);
522
+ fieldValues.set("_pin", inst?.fieldValues.get("_pin") ?? inst?.fieldValues.get("pin") ?? pinArg.text);
523
+ }
524
+ else {
525
+ fieldValues.set("_pin", pinArg.getText());
526
+ }
527
+ }
528
+ if (className === "GPIO" && ctorArgs && ctorArgs.length >= 2) {
529
+ // "GPIO.OUTPUT | GPIO.PULL_UP" — the token text is the IR carrier.
530
+ fieldValues.set("_flags", ctorArgs[1].getText());
531
+ }
532
+ const thinOptsCtor = { PWM: true, ADC: true, DAC: true, Counter: true };
533
+ if (thinOptsCtor[className] && ctorArgs && ctorArgs.length >= 2 && ts.isObjectLiteralExpression(ctorArgs[1])) {
534
+ for (const prop of ctorArgs[1].properties) {
535
+ if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name))
536
+ continue;
537
+ const name = prop.name.text;
538
+ if (ts.isNumericLiteral(prop.initializer)) {
539
+ fieldValues.set(`_${name}`, prop.initializer.text.replace(/_/g, ""));
540
+ }
541
+ else {
542
+ // Token (ADC.GAIN_1_4) or runtime expression text.
543
+ fieldValues.set(`_${name}`, prop.initializer.getText());
544
+ }
545
+ }
546
+ }
547
+ if (className === "Watchdog" && ctorArgs && ctorArgs.length >= 1 && ts.isNumericLiteral(ctorArgs[0])) {
548
+ fieldValues.set("_timeoutMs", ctorArgs[0].text.replace(/_/g, ""));
549
+ }
550
+ // Thin File/Mqtt: the path / broker-uri + client-id facts.
551
+ if (className === "File" && ctorArgs && ctorArgs.length >= 1 && ts.isStringLiteral(ctorArgs[0])) {
552
+ fieldValues.set("_path", ctorArgs[0].text);
553
+ }
554
+ if (className === "Mqtt" && ctorArgs && ctorArgs.length >= 1 && ts.isStringLiteral(ctorArgs[0])) {
555
+ fieldValues.set("_uri", ctorArgs[0].text);
556
+ const mopts = ctorArgs[1];
557
+ if (mopts && ts.isObjectLiteralExpression(mopts)) {
558
+ for (const prop of mopts.properties) {
559
+ if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)
560
+ && prop.name.text === "clientId" && ts.isStringLiteral(prop.initializer)) {
561
+ fieldValues.set("_clientId", prop.initializer.text);
562
+ }
563
+ }
564
+ }
565
+ }
566
+ // Thin Store: the namespace is the construction fact (plain text —
567
+ // the plugin case quotes it when composing the settings key).
568
+ if (className === "Store" && ctorArgs && ctorArgs.length >= 1 && ts.isStringLiteral(ctorArgs[0])) {
569
+ fieldValues.set("_ns", ctorArgs[0].text);
570
+ }
571
+ // Thin BLE: the advertised name is the construction fact.
572
+ if (className === "BLE" && ctorArgs && ctorArgs.length >= 1 && ts.isStringLiteral(ctorArgs[0])) {
573
+ fieldValues.set("_name", JSON.stringify(ctorArgs[0].text));
574
+ }
575
+ if (className === "Counter" && ctorArgs && ctorArgs.length >= 1 && ts.isNumericLiteral(ctorArgs[0])) {
576
+ fieldValues.set("_instance", ctorArgs[0].text.replace(/_/g, ""));
577
+ }
578
+ // Thin WiFi station: first arg is the ssid literal; the opts carry
579
+ // the link facts. Nested ipv4 facts flatten to _ipAddr/_gateway/
580
+ // _netmask. Computed defaults (security ternary, timeoutMs ??) are
581
+ // applied by the wifiJoin plugin case, not here.
582
+ if (className === "WiFi" && ctorArgs && ctorArgs.length >= 1) {
583
+ if (ts.isStringLiteral(ctorArgs[0])) {
584
+ fieldValues.set("_ssid", JSON.stringify(ctorArgs[0].text));
585
+ }
586
+ else {
587
+ fieldValues.set("_ssid", ctorArgs[0].getText());
588
+ }
589
+ const wopts = ctorArgs[1];
590
+ if (wopts && ts.isObjectLiteralExpression(wopts)) {
591
+ for (const prop of wopts.properties) {
592
+ if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name))
593
+ continue;
594
+ const name = prop.name.text;
595
+ if (name === "ipv4" && ts.isObjectLiteralExpression(prop.initializer)) {
596
+ for (const ip of prop.initializer.properties) {
597
+ if (!ts.isPropertyAssignment(ip) || !ts.isIdentifier(ip.name))
598
+ continue;
599
+ const ipName = ip.name.text;
600
+ const key = ipName === "addr" ? "_ipAddr" : `_${ipName}`;
601
+ if (ts.isStringLiteral(ip.initializer)) {
602
+ fieldValues.set(key, JSON.stringify(ip.initializer.text));
603
+ }
604
+ else {
605
+ fieldValues.set(key, ip.initializer.getText());
606
+ }
607
+ }
608
+ }
609
+ else if (name !== "powerSave") {
610
+ if (ts.isStringLiteral(prop.initializer)) {
611
+ fieldValues.set(`_${name}`, JSON.stringify(prop.initializer.text));
612
+ }
613
+ else if (ts.isNumericLiteral(prop.initializer)) {
614
+ fieldValues.set(`_${name}`, prop.initializer.text.replace(/_/g, ""));
615
+ }
616
+ else {
617
+ fieldValues.set(`_${name}`, prop.initializer.getText());
618
+ }
619
+ }
620
+ else {
621
+ // powerSave: WiFi.PS_OFF token → ps=1 flag (0 = default on).
622
+ const txt = prop.initializer.getText();
623
+ fieldValues.set("_ps", txt.includes("PS_OFF") ? "1" : "0");
624
+ }
625
+ }
626
+ }
627
+ }
628
+ if (className === "Thread" && ctorArgs && ctorArgs.length >= 1 && ts.isNumericLiteral(ctorArgs[0])) {
629
+ fieldValues.set("_index", ctorArgs[0].text.replace(/_/g, ""));
630
+ // stackKb → bytes here (the class-body arithmetic is erased).
631
+ const kbArg = ctorArgs.length >= 2 && ts.isObjectLiteralExpression(ctorArgs[1])
632
+ ? ctorArgs[1].properties.find((p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === "stackKb")
633
+ : undefined;
634
+ const kb = kbArg && ts.isPropertyAssignment(kbArg) && ts.isNumericLiteral(kbArg.initializer)
635
+ ? parseInt(kbArg.initializer.text.replace(/_/g, ""), 10) : 2;
636
+ fieldValues.set("_stackBytes", String((isNaN(kb) ? 2 : kb) * 1024));
637
+ }
638
+ // Tier-2 thin buses. The first arg is the board's bus/port instance
639
+ // export (I2C0/SPI0/UART1) — kept as text, which the lowerings'
640
+ // parseControllerIndex reads; SPITarget adds the cs pin (identifier
641
+ // or property access, resolved later like every pin arg); opts carry
642
+ // the wire facts (hz/mode/baud).
643
+ const thinBusFirst = { I2CTarget: true, SPITarget: true, UART: true };
644
+ if (thinBusFirst[className] && ctorArgs && ctorArgs.length >= 1) {
645
+ const busArg = ctorArgs[0];
646
+ if (ts.isIdentifier(busArg)) {
647
+ const inst = halInstances.get(busArg.text);
648
+ fieldValues.set("_bus", inst?.fieldValues.get("_bus") ?? busArg.text);
649
+ fieldValues.set("_port", inst?.fieldValues.get("_port") ?? inst?.fieldValues.get("_bus") ?? busArg.text);
650
+ }
651
+ else if (ts.isStringLiteral(busArg)) {
652
+ fieldValues.set("_bus", busArg.text);
653
+ fieldValues.set("_port", busArg.text);
654
+ }
655
+ else {
656
+ fieldValues.set("_bus", busArg.getText());
657
+ fieldValues.set("_port", busArg.getText());
658
+ }
659
+ }
660
+ if (className === "SPITarget" && ctorArgs && ctorArgs.length >= 2) {
661
+ const csArg = ctorArgs[1];
662
+ if (ts.isIdentifier(csArg)) {
663
+ const inst = halInstances.get(csArg.text);
664
+ fieldValues.set("_cs", inst?.fieldValues.get("_pin") ?? inst?.fieldValues.get("pin") ?? csArg.text);
665
+ }
666
+ else {
667
+ fieldValues.set("_cs", csArg.getText());
668
+ }
669
+ }
670
+ if (className === "I2CTarget" && ctorArgs && ctorArgs.length >= 2 && ts.isNumericLiteral(ctorArgs[1])) {
671
+ fieldValues.set("_address", ctorArgs[1].text);
672
+ }
673
+ const thinBusOpts = { I2CTarget: true, SPITarget: true, UART: true, Thread: true };
674
+ if (thinBusOpts[className] && ctorArgs && ctorArgs.length >= 2) {
675
+ // I2CTarget/UART: (bus, opts?); SPITarget: (bus, cs, opts?).
676
+ const optsArg = ctorArgs.length === 2 ? ctorArgs[1] : ctorArgs[2];
677
+ if (optsArg && ts.isObjectLiteralExpression(optsArg)) {
678
+ for (const prop of optsArg.properties) {
679
+ if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name))
680
+ continue;
681
+ const name = prop.name.text;
682
+ if (ts.isNumericLiteral(prop.initializer)) {
683
+ fieldValues.set(`_${name}`, prop.initializer.text.replace(/_/g, ""));
684
+ }
685
+ else {
686
+ fieldValues.set(`_${name}`, prop.initializer.getText());
687
+ }
688
+ }
689
+ }
690
+ }
691
+ // Thin-class field DEFAULTS (the singleton __default_fields channel
692
+ // does not reach constructed instances). Fill only absent fields —
693
+ // an omitted opts object must resolve to these sentinels, not leak
694
+ // `this->_gain` text into the ops (string args pass through raw).
695
+ const thinFieldDefaults = {
696
+ ADC: { _gain: "", _reference: "" },
697
+ I2CTarget: { _hz: "0" },
698
+ SPITarget: { _hz: "1000000", _mode: "0" },
699
+ UART: { _baud: "115200", _rxBufferBytes: "64" },
700
+ DAC: { _resolution: "0" },
701
+ };
702
+ // WiFi absent-fact sentinels: fields whose class initializer is
703
+ // `opts.x?.y` render as raw `this->_x` when the fact was omitted.
704
+ // "undefined" is what dropUndefined() strips at the plugin case,
705
+ // so the op omits the fact entirely.
706
+ if (className === "WiFi") {
707
+ const wifiDefaults = {
708
+ _psk: "undefined", _ipAddr: "undefined", _gateway: "undefined",
709
+ _netmask: "undefined", _timeoutMs: "15000", _channel: "0",
710
+ _band: "0", _ps: "0",
711
+ };
712
+ for (const [k, v] of Object.entries(wifiDefaults)) {
713
+ if (!fieldValues.has(k))
714
+ fieldValues.set(k, v);
715
+ }
716
+ }
717
+ const thinDefs = thinFieldDefaults[className];
718
+ if (thinDefs) {
719
+ for (const [k, v] of Object.entries(thinDefs)) {
720
+ if (!fieldValues.has(k))
721
+ fieldValues.set(k, v);
722
+ }
472
723
  }
473
724
  if (ctorArgs) {
474
725
  // Resolve a Pin-identifier ctor arg to its _pin number, for any HAL
@@ -503,8 +754,8 @@ export function variableStatementToIR(statement, fileName, sourceText, diagnosti
503
754
  if (result && (!isOwnershipMethod || isSingletonReceiver)) {
504
755
  const isHalOpReturn = result.returnValue === "__hal_op_return__";
505
756
  // A __TYPED_ARRAY__ return comes ONLY from a HAL method body
506
- // (`return new Uint8Array(count)` in e.g. I2CDevice.readBytes /
507
- // SPIDevice.readRegister). The method's side-effect HAL ops (the
757
+ // (`return new Uint8Array(count)` in buffer-returning device
758
+ // methods). The method's side-effect HAL ops (the
508
759
  // i2c.read_buffer / spi.read_buffer fill loops) write INTO this buffer
509
760
  // via the __HAL_READ_BUF__ placeholder (rewritten to `varName`). So the
510
761
  // buffer var_decl MUST precede the fill ops in emitted order — at top
@@ -568,9 +819,16 @@ export function variableStatementToIR(statement, fileName, sourceText, diagnosti
568
819
  const instance = resolveHALReceiver(receiver);
569
820
  const fieldValues = new Map(instance?.fieldValues || []);
570
821
  // For device() factory calls, copy the address/cs from the call arg
571
- // so I2CDevice/SPIDevice methods can resolve this._address / this._cs.
572
- if (result.returnClassName === "I2CDevice" || result.returnClassName === "SPIDevice") {
573
- const fieldName = result.returnClassName === "SPIDevice" ? "_cs" : "_address";
822
+ // so I2CTarget/SPITarget methods can resolve this._address / this._cs.
823
+ if (result.returnClassName === "I2CTarget" || result.returnClassName === "SPITarget") {
824
+ const isSpiTarget = result.returnClassName === "SPITarget";
825
+ const fieldName = isSpiTarget ? "_cs" : "_address";
826
+ // The targets' __default_fields channel does not reach factory
827
+ // results; carry the wire defaults so this._hz / this._mode
828
+ // resolve in the inlined method bodies.
829
+ fieldValues.set("_hz", isSpiTarget ? "1000000" : "0");
830
+ if (isSpiTarget)
831
+ fieldValues.set("_mode", "0");
574
832
  const firstArg = init.arguments?.[0];
575
833
  if (firstArg) {
576
834
  if (ts.isNumericLiteral(firstArg))
@@ -585,19 +843,6 @@ export function variableStatementToIR(statement, fileName, sourceText, diagnosti
585
843
  }
586
844
  }
587
845
  }
588
- // For Http factory calls (const req = Http.get(url)), record the
589
- // HTTP verb + URL so req.send() can resolve this._method/this._url.
590
- if (result.returnClassName === "HttpRequest") {
591
- const verb = httpFactoryVerb(init.expression.name.text);
592
- if (verb)
593
- fieldValues.set("_method", verb);
594
- const urlArg = init.arguments?.[0];
595
- if (urlArg) {
596
- const urlText = httpUrlArgText(urlArg);
597
- if (urlText)
598
- fieldValues.set("_url", urlText);
599
- }
600
- }
601
846
  halInstances.set(varName, {
602
847
  className: result.returnClassName,
603
848
  fieldValues,
@@ -634,11 +879,16 @@ export function variableStatementToIR(statement, fileName, sourceText, diagnosti
634
879
  }
635
880
  }
636
881
  if (result.returnValue && result.returnValue !== "this") {
882
+ const lastOp = result.halOps[result.halOps.length - 1];
883
+ // A double-returning HAL op makes the captured var floating
884
+ // (sensor.get → val1 + val2/1e6): the snprintf specifier picker
885
+ // reads floatVariables and must pick %g, not %d.
886
+ if (lastOp && lastOp.operation === "sensor.get")
887
+ registerFloatVariable(varName);
637
888
  if (isHalOpReturn) {
638
- const lastOp = result.halOps[result.halOps.length - 1];
639
889
  lowered.push({
640
890
  kind: "var_decl",
641
- sourceSpan: makeSourceSpan(declaration, fileName, sourceText),
891
+ sourceSpan: makeSourceSpan(declaration.initializer, fileName, sourceText),
642
892
  leadingComments: commentsAssigned ? [] : statementComments.leadingComments,
643
893
  trailingComments: [],
644
894
  name: varName,
@@ -703,11 +953,20 @@ export function variableStatementToIR(statement, fileName, sourceText, diagnosti
703
953
  }
704
954
  }
705
955
  }
956
+ // A typed-array buffer declared `const` stays writable storage: HAL fills
957
+ // write through it (e.g. spi.transceive's rx buffer), so the emitted C
958
+ // array must not be const — mirroring the __TYPED_ARRAY__ return branch.
959
+ // Keeping the source storage would split the definition (`uint8_t id[]`,
960
+ // the array renderer drops the qualifier) from the split-mode extern
961
+ // (`extern const uint8_t id[]`) — a conflicting declaration.
962
+ let effectiveStorage = storage;
706
963
  if (actualInitializer && ts.isNewExpression(actualInitializer)) {
707
964
  const ctorText = actualInitializer.expression && ts.isIdentifier(actualInitializer.expression)
708
965
  ? actualInitializer.expression.text : "";
709
966
  if (TYPED_ARRAY_ELEMENT_MAP[ctorText] && ts.isIdentifier(declaration.name)) {
710
967
  activeCArrayVars.add(declaration.name.text);
968
+ if (effectiveStorage === "const")
969
+ effectiveStorage = "let";
711
970
  }
712
971
  }
713
972
  let lambdaInitializer;
@@ -742,7 +1001,7 @@ export function variableStatementToIR(statement, fileName, sourceText, diagnosti
742
1001
  leadingComments: commentsAssigned ? [] : statementComments.leadingComments,
743
1002
  trailingComments: commentsAssigned ? [] : statementComments.trailingComments,
744
1003
  name: declaration.name.text,
745
- storage,
1004
+ storage: effectiveStorage,
746
1005
  cppType: "auto",
747
1006
  isVolatile,
748
1007
  initializer: lambdaInitializer ?? (actualInitializer
@@ -1161,3 +1420,40 @@ export function collectPointerVars(statements) {
1161
1420
  }
1162
1421
  return pointerVars;
1163
1422
  }
1423
+ /**
1424
+ * Resolve the second `new Sensor(part, <dev>)` argument to an I2C bus name +
1425
+ * 7-bit address. Accepts the inline factory form `I2C1.device(0x44)` and an
1426
+ * identifier previously bound to one (`const dev = I2C1.device(0x44)`). The
1427
+ * general receiver resolution carries the bus fields through `device()`'s
1428
+ * I2CTarget return type; the inline form's address is taken from the call's
1429
+ * numeric argument (the general path does not extract call args).
1430
+ */
1431
+ function resolveSensorDeviceArg(arg) {
1432
+ const inst = resolveHALReceiver(arg);
1433
+ const portField = inst?.className === "SPITarget" ? "_cs" : "_address";
1434
+ if (inst && (inst.className === "I2CTarget" || inst.className === "SPITarget") && inst.fieldValues.has("_bus") && inst.fieldValues.has(portField)) {
1435
+ return { bus: inst.fieldValues.get("_bus"), port: inst.fieldValues.get(portField), kind: inst.className === "SPITarget" ? "spi" : "i2c" };
1436
+ }
1437
+ // Inline bus.device(<port>) call: the general receiver path carries the bus
1438
+ // fields through device()'s return type but drops the argument — take the
1439
+ // port from the call's numeric literal.
1440
+ if (ts.isCallExpression(arg) && ts.isPropertyAccessExpression(arg.expression) && arg.expression.name.text === "device") {
1441
+ const args = arg.arguments ?? [];
1442
+ const numericArg = args.find((a) => ts.isNumericLiteral(a));
1443
+ // SPI CS arrives as a pin identifier (SPI0.device(PA4)) — resolve it to
1444
+ // its number the same way the device() chain path does.
1445
+ const pinArg = args.find((a) => ts.isIdentifier(a));
1446
+ const pinInst = pinArg && ts.isIdentifier(pinArg) ? halInstances.get(pinArg.text) : undefined;
1447
+ const portText = numericArg && ts.isNumericLiteral(numericArg)
1448
+ ? numericArg.text
1449
+ : (pinInst?.fieldValues.get("_pin") ?? pinInst?.fieldValues.get("pin"));
1450
+ if (inst && inst.fieldValues.has("_bus") && portText !== undefined) {
1451
+ // The bus kind follows the receiver's resolved class when it round-
1452
+ // tripped; otherwise infer from the bus alias (SPI/SPI<n> vs Wire).
1453
+ const busName = inst.fieldValues.get("_bus");
1454
+ const kind = inst.className === "SPITarget" || /^SPI/.test(busName) ? "spi" : "i2c";
1455
+ return { bus: busName, port: portText, kind };
1456
+ }
1457
+ }
1458
+ return null;
1459
+ }
@@ -3,7 +3,8 @@
3
3
  //
4
4
  // Validates that try/catch and throw statements are not used on architectures
5
5
  // that do not support C++ exceptions. The platform strategy determines which
6
- // architectures have exceptions disabled (e.g. AVR-GCC with -fno-exceptions).
6
+ // architectures have exceptions disabled (e.g. Zephyr with
7
+ // CONFIG_CPP_EXCEPTIONS=n).
7
8
  // ---------------------------------------------------------------------------
8
9
  /**
9
10
  * Validate that try/catch and throw statements are not used on
@@ -34,7 +35,7 @@ export function validateTryCatch(program, boardConstants, strategy) {
34
35
  severity: 'error',
35
36
  code: 'try-catch-unsupported',
36
37
  message: `try/catch is not supported on ${arch.toUpperCase()} targets. ` +
37
- `AVR-GCC disables C++ exceptions (-fno-exceptions) and the generated ` +
38
+ `C++ exceptions are disabled (-fno-exceptions) on this target and the generated ` +
38
39
  `code will not compile. Use return-code error checking instead.`,
39
40
  hint: `function readSensor(): number | null {\n const data = sensor.read();\n if (!data) return null; // error path\n return data.value; // success path\n}`,
40
41
  line: stmt.sourceSpan?.startLine,
@@ -53,7 +54,7 @@ export function validateTryCatch(program, boardConstants, strategy) {
53
54
  severity: 'error',
54
55
  code: 'try-catch-unsupported',
55
56
  message: `throw is not supported on ${arch.toUpperCase()} targets. ` +
56
- `AVR-GCC disables C++ exceptions (-fno-exceptions). ` +
57
+ `C++ exceptions are disabled (-fno-exceptions) on this target. ` +
57
58
  `Use return codes or error flags instead.`,
58
59
  hint: `return null; // or return an error code`,
59
60
  line: stmt.sourceSpan?.startLine,
@@ -38,7 +38,7 @@ const BUS_INTERFACE_TYPE_NAMES = new Set([
38
38
  "I2CAddress", "SPITransferOptions",
39
39
  ]);
40
40
  const STRATEGY_TYPE_NAMES = new Set([
41
- "NativeStrategy", "ArduinoStrategy", "BoardStrategy",
41
+ "NativeStrategy", "ZephyrStrategy", "BoardStrategy",
42
42
  "RuntimePolyfillIR", "PeripheralUsage",
43
43
  ]);
44
44
  const BOARD_CONSTANT_TYPE_NAMES = new Set([
@@ -50,7 +50,8 @@ const OWNERSHIP_WRAPPER_TYPE_NAMES = new Set([
50
50
  ]);
51
51
  /** Safety wrapper types: like ownership wrappers (phantom TS types that carry
52
52
  * a type parameter), but instead of stripping the wrapper, the C++ keeps it
53
- * as a template instantiation: SafeVariable<number> → SafeVariable<int32_t>.
53
+ * as a template instantiation: SafeVariable<number> → SafeVariable<double>,
54
+ * SafeInt<number> → SafeInt<int32_t> (integer-only by contract).
54
55
  * The C++ template definition is provided by the safety polyfill. */
55
56
  const SAFE_WRAPPER_TYPE_NAMES = new Set([
56
57
  "SafeVariable",
@@ -158,10 +159,20 @@ export function typeNodeToCppType(node, typeAliases, typeParametersInScope) {
158
159
  // SafeVariable<T> is a safety wrapper: unlike ownership wrappers (which
159
160
  // strip the wrapper name entirely), SafeVariable keeps the wrapper name
160
161
  // in the C++ type because the polyfill provides a template definition.
161
- // SafeVariable<number> → SafeVariable<int> (the template is emitted by
162
- // the safety polyfill's helperStructs).
162
+ // SafeVariable<number> → SafeVariable<double> (the keyword's general
163
+ // mapping; the polyfill ships float/double specializations).
163
164
  if (SAFE_WRAPPER_TYPE_NAMES.has(wrapperName)) {
164
165
  const innerTypeNode = node.typeArguments?.[0];
166
+ // SafeInt is signed-integer-only by contract — its polyfill carries a
167
+ // static_assert rejecting floating-point T. The TS `number` keyword
168
+ // (and SafeInt's implicit `T = number` default) therefore resolves to
169
+ // int32_t, NOT the general number→double mapping. An explicit float /
170
+ // double annotation still passes through so the static_assert fires as
171
+ // designed.
172
+ if (wrapperName === "SafeInt" &&
173
+ (innerTypeNode === undefined || innerTypeNode.kind === ts.SyntaxKind.NumberKeyword)) {
174
+ return "SafeInt<int32_t>";
175
+ }
165
176
  const innerCppType = typeNodeToCppType(innerTypeNode, typeAliases, typeParametersInScope);
166
177
  return `${wrapperName}<${innerCppType}>`;
167
178
  }
@@ -1,7 +1,6 @@
1
1
  import { resolveStrategy } from "../platform/registry.js";
2
2
  import { hasLoadedFramework, getLoadedFramework } from "../framework-registry.js";
3
3
  import { analyzeInterruptSafety, inferVolatileForIsrSharedVars, detectReentrancyRisk } from "./interrupt-analysis.js";
4
- import { analyzeWorkerIsolation } from "./worker-analysis.js";
5
4
  import { validateADCRange } from "./adc-range-validation.js";
6
5
  import { createEmptyPeripheralUsage } from "./peripheral-usage.js";
7
6
  import { validatePeripherals } from "./peripheral-validation.js";
@@ -11,8 +10,6 @@ import { validatePinAliasConflicts } from "./pin-alias-conflict.js";
11
10
  import { validatePinModeConfig } from "./pin-mode-validation.js";
12
11
  import { validateUnsafePins } from "./pin-safety.js";
13
12
  import { validatePulldownSupport } from "./pulldown-validation.js";
14
- import { validatePWMTimerSharing } from "./pwm-timer-sharing.js";
15
- import { validateTimer0PWMTimingConflict } from "./timer0-pwm-timing-conflict.js";
16
13
  import { validateTryCatch } from "./try-catch-validation.js";
17
14
  import { validateMemoryBudget } from "./memory-budget-validation.js";
18
15
  import { validateBlockingDelayInLoop } from "./timing-validation.js";
@@ -29,16 +26,10 @@ export function runProgramValidations(program, strategy) {
29
26
  diagnostics.push(...validateUnsafePins(peripheralUsage, program.boardConstants, program.fileName));
30
27
  diagnostics.push(...analyzeResources(program, resolvedStrategy));
31
28
  diagnostics.push(...validatePinAliasConflicts(peripheralUsage, program.boardConstants, program.fileName));
32
- diagnostics.push(...validatePWMTimerSharing(peripheralUsage, program.boardConstants, program.fileName));
33
- diagnostics.push(...validateTimer0PWMTimingConflict(peripheralUsage, program.boardConstants, program.fileName));
34
29
  diagnostics.push(...validatePulldownSupport(peripheralUsage, program.boardConstants, program.fileName));
35
30
  diagnostics.push(...analyzeInterruptSafety(program, peripheralUsage, resolvedStrategy.isrUnsafeOperations?.()));
36
31
  inferVolatileForIsrSharedVars(program, diagnostics);
37
32
  detectReentrancyRisk(program, diagnostics);
38
- // Worker isolation: bus access in workers is a hard error; worker-shared
39
- // globals get volatile promotion (info) or a data-race warning. Only fires
40
- // when worker.submit ops are present, so non-worker programs are unaffected.
41
- analyzeWorkerIsolation(program, diagnostics);
42
33
  diagnostics.push(...validateADCRange(program, program.boardConstants));
43
34
  diagnostics.push(...validateUnitSuspicion(program));
44
35
  diagnostics.push(...validatePinModeConfig(program));
@@ -32,12 +32,3 @@ export declare function generateDeclsForDirectory(dir: string, recursive?: boole
32
32
  * a later step for the directory-scan path.
33
33
  */
34
34
  export declare function generateDecl(filePath: string, outputPath?: string): string | null;
35
- import { type ComponentScanRoots } from "./component-discovery.js";
36
- /**
37
- * Generate .d.ts declarations for every declared component's headers,
38
- * picking the C or C++ emitter per header based on whether it declares classes.
39
- *
40
- * Returns the list of created/updated .d.ts paths (headers that produced no
41
- * declarations are skipped).
42
- */
43
- export declare function generateComponentDeclsForProject(projectDir: string, roots: ComponentScanRoots): string[];