@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
@@ -0,0 +1,314 @@
1
+ import * as readline from "node:readline/promises";
2
+ import { stdin as input, stdout as output } from "node:process";
3
+ import { execSync } from "node:child_process";
4
+ import { readdirSync } from "node:fs";
5
+ import chalk from "chalk";
6
+ import { KNOWN_TARGETS } from "./scaffold.js";
7
+ import { findPackBoard, packBoardAsTarget } from "./pack-targets.js";
8
+ import { pickTarget } from "./board-search.js";
9
+ import { activeBoardCatalog } from "../board-catalog/index.js";
10
+ import { frameworksForTarget, frameworkCatalogEntry, frameworkCompatibleWithTarget, FRAMEWORK_CATALOG, frameworkTargetProfile, probeMethodsForBoard } from './framework-catalog.js';
11
+ /**
12
+ * Attached serial ports for the wizard's port question — dependency-free
13
+ * (the wizard runs before any project deps exist, and cuttlefish itself
14
+ * carries no native serialport binding). Windows asks the .NET SerialPort
15
+ * class via PowerShell; macOS/Linux scan /dev for USB CDC/bridge nodes.
16
+ * Returns [] when enumeration fails — the question then falls back to a
17
+ * free-text prompt with the platform hint as default.
18
+ */
19
+ export function detectSerialPorts() {
20
+ try {
21
+ if (process.platform === "win32") {
22
+ const out = execSync("[System.IO.Ports.SerialPort]::GetPortNames() -join ','", { shell: "powershell.exe", stdio: ["ignore", "pipe", "ignore"], timeout: 5000 }).toString();
23
+ return out.split(",").map((s) => s.trim()).filter((s) => /^[Cc][Oo][Mm]\d+$/.test(s));
24
+ }
25
+ const dev = readdirSync("/dev");
26
+ return dev
27
+ .filter((n) => /^tty(ACM|USB)/.test(n) || /^cu\.(usb|USB|modem)/.test(n))
28
+ .map((n) => (process.platform === "darwin" ? `/dev/${n}` : `/dev/${n}`))
29
+ .sort();
30
+ }
31
+ catch {
32
+ return [];
33
+ }
34
+ }
35
+ function validateProjectName(name) {
36
+ if (!name || name.trim().length === 0) {
37
+ return "Project name cannot be empty.";
38
+ }
39
+ const normalized = name.trim().toLowerCase();
40
+ if (!/^[a-z][a-z0-9-]*$/.test(normalized)) {
41
+ return "Project name must start with a letter and contain only lowercase letters, digits, and hyphens.";
42
+ }
43
+ if (normalized.length > 64) {
44
+ return "Project name must be 64 characters or fewer.";
45
+ }
46
+ return null;
47
+ }
48
+ async function promptText(rl, prompt, defaultValue, validate) {
49
+ while (true) {
50
+ const suffix = defaultValue ? ` (${defaultValue})` : "";
51
+ const answer = await rl.question(`${chalk.cyan("?")} ${prompt}${suffix}: `);
52
+ const value = (answer.trim() || (defaultValue ?? "")).trim();
53
+ if (validate) {
54
+ const error = validate(value);
55
+ if (error) {
56
+ console.log(` ${chalk.red("✗")} ${error}`);
57
+ continue;
58
+ }
59
+ }
60
+ return value;
61
+ }
62
+ }
63
+ async function promptSelect(rl, prompt, options) {
64
+ console.log(`${chalk.cyan("?")} ${prompt}:`);
65
+ for (let i = 0; i < options.length; i++) {
66
+ console.log(` ${chalk.dim(`${i + 1})`)} ${options[i].label}`);
67
+ }
68
+ while (true) {
69
+ const answer = await rl.question(` Enter number (1-${options.length}): `);
70
+ const idx = parseInt(answer.trim(), 10) - 1;
71
+ if (idx >= 0 && idx < options.length) {
72
+ return options[idx].value;
73
+ }
74
+ console.log(` ${chalk.red("✗")} Please enter a number between 1 and ${options.length}.`);
75
+ }
76
+ }
77
+ async function promptConfirm(rl, prompt, defaultValue) {
78
+ const suffix = defaultValue ? " (Y/n)" : " (y/N)";
79
+ const answer = await rl.question(`${chalk.cyan("?")} ${prompt}${suffix}: `);
80
+ const trimmed = answer.trim().toLowerCase();
81
+ if (trimmed === "")
82
+ return defaultValue;
83
+ return trimmed === "y" || trimmed === "yes";
84
+ }
85
+ export async function runCreateWizard(partialOptions) {
86
+ let rl = readline.createInterface({ input, output });
87
+ try {
88
+ console.log();
89
+ console.log(chalk.cyan("⤳ Cuttlefish") + chalk.dim(" — Project Setup"));
90
+ console.log();
91
+ // 1. Project name
92
+ const projectName = partialOptions?.projectName
93
+ ?? await promptText(rl, "Project name", "my-project", validateProjectName);
94
+ // 2. Target selection — the filterable board picker covers everything:
95
+ // native, bare silicon (soc entries), and every Zephyr board from the
96
+ // catalog. One search box, no two-stage list. A preseeded --board that
97
+ // resolves skips the picker entirely.
98
+ let packBoard;
99
+ let foundTarget;
100
+ if (partialOptions?.board) {
101
+ const found = KNOWN_TARGETS.find((t) => t.id === partialOptions.board);
102
+ const packFound = found ? undefined : findPackBoard(partialOptions.board);
103
+ if (found) {
104
+ foundTarget = found;
105
+ console.log(`${chalk.cyan("?")} Target: ${chalk.white(found.displayName)} (${chalk.dim(partialOptions.board)})`);
106
+ }
107
+ else if (packFound) {
108
+ packBoard = packFound;
109
+ console.log(`${chalk.cyan("?")} Target: ${chalk.white(packFound.name)} (${chalk.dim(packFound.identifier)})`);
110
+ }
111
+ }
112
+ if (Object.keys(activeBoardCatalog()).length === 0) {
113
+ console.log(` ${chalk.yellow("!")} No board catalog on this machine — the catalog is generated`);
114
+ console.log(` from your Zephyr tree. Run the installer or 'cuttlefish board sync' to`);
115
+ console.log(` list boards; a preseeded --board <target> still works and validates at`);
116
+ console.log(` the first build.`);
117
+ }
118
+ if (!foundTarget && !packBoard) {
119
+ // Close the readline interface for the filterable select (it owns
120
+ // stdin in raw mode), then recreate it for the remaining prompts.
121
+ // Closing a readline pauses stdin; resume it so the select's internal
122
+ // readline receives character bytes (arrow keys arrive via keypress
123
+ // events either way, but typed characters need flowing mode).
124
+ rl.close();
125
+ input.resume();
126
+ const pick = await pickTarget();
127
+ rl = readline.createInterface({ input, output });
128
+ if (!pick) {
129
+ console.log(` ${chalk.yellow("!")} Target selection cancelled.`);
130
+ return null;
131
+ }
132
+ if (pick.kind === 'native') {
133
+ foundTarget = KNOWN_TARGETS.find((t) => t.isNative);
134
+ }
135
+ else if (pick.kind === 'mcu') {
136
+ // Bare-silicon targets were removed with the curated soc layer.
137
+ console.log(` ${chalk.yellow("!")} Bare-silicon targets no longer exist — pick a board from the catalog.`);
138
+ }
139
+ else {
140
+ packBoard = pick.entry;
141
+ console.log(`${chalk.cyan("?")} Target: ${chalk.white(pick.entry.name)} (${chalk.dim(pick.entry.identifier)})`);
142
+ }
143
+ }
144
+ const target = packBoard
145
+ ? packBoardAsTarget(packBoard)
146
+ : foundTarget;
147
+ // 3. Framework. Narrow to the frameworks compatible with the selected board
148
+ // (see framework-catalog), then let the user pick. The chosen package is
149
+ // installed into the new project by `cuttlefish create`, so we offer every
150
+ // compatible framework regardless of what is currently installed — no
151
+ // require.resolve discovery (which also avoided an ESM `require` pitfall
152
+ // where the lookup always failed and made the wizard dead-end).
153
+ let framework = target.framework;
154
+ let frameworkPackage = target.frameworkPackage;
155
+ const compatible = frameworksForTarget(target)
156
+ .filter((f) => f.installable);
157
+ if (compatible.length === 0) {
158
+ // Defensive: every known target maps to at least one framework in the catalog.
159
+ throw new Error(`No installable frameworks are compatible with target '${target.id}'.`);
160
+ }
161
+ if (partialOptions?.framework) {
162
+ const requested = frameworkCatalogEntry(partialOptions.framework);
163
+ if (!requested) {
164
+ const available = FRAMEWORK_CATALOG.filter(f => f.installable).map(f => f.id).join(", ");
165
+ throw new Error(`Unknown framework '${partialOptions.framework}'. Available: ${available}`);
166
+ }
167
+ // Same compatibility guard as the non-interactive CLI path: a preseeded
168
+ // --framework bypasses the narrowing below, so validate it explicitly.
169
+ if (!frameworkCompatibleWithTarget(target, requested.id)) {
170
+ const list = compatible.map(f => f.id).join(", ");
171
+ throw new Error(`Framework '${requested.id}' is not compatible with target '${target.id}' (${target.displayName}). ` +
172
+ `Compatible frameworks: ${list}`);
173
+ }
174
+ framework = requested.id;
175
+ frameworkPackage = requested.packageName;
176
+ console.log(`${chalk.cyan("?")} Framework: ${chalk.white(requested.label)}`);
177
+ }
178
+ else if (compatible.length === 1) {
179
+ framework = compatible[0].id;
180
+ frameworkPackage = compatible[0].packageName;
181
+ console.log(`${chalk.cyan("?")} Framework: ${chalk.white(compatible[0].label)}`);
182
+ }
183
+ else {
184
+ const selected = await promptSelect(rl, "Framework", compatible.map((f) => ({ label: f.label, value: f.id })));
185
+ const match = compatible.find((f) => f.id === selected);
186
+ framework = match.id;
187
+ frameworkPackage = match.packageName;
188
+ }
189
+ // Resolve the framework-specific build target + toolchain (the Zephyr
190
+ // board id + 'west'). See framework-catalog.
191
+ const profile = frameworkTargetProfile(target, framework);
192
+ let buildTarget = profile.buildTarget ?? target.buildTarget;
193
+ let zephyrCustomBoard = false;
194
+ // 3.5 Probe method (Zephyr boards that ship a table). The probe in the
195
+ // user's hand becomes the scaffolded zephyr.probe entry — it serves both
196
+ // flashing and debugging; "board default" omits the section.
197
+ let probeMethod;
198
+ const probeMethods = framework === 'zephyr' ? probeMethodsForBoard(target.id) : [];
199
+ if (probeMethods.length > 0) {
200
+ if (partialOptions?.probe) {
201
+ probeMethod = partialOptions.probe;
202
+ console.log(`${chalk.cyan("?")} Probe method: ${chalk.white(probeMethod)}`);
203
+ }
204
+ else {
205
+ const chosen = await promptSelect(rl, "Which probe will you attach to this board?", [
206
+ ...probeMethods.map((m) => ({ label: `${m.id} — ${m.description}`, value: m.id })),
207
+ { label: "board default (no zephyr.probe entry)", value: "" },
208
+ ]);
209
+ probeMethod = chosen === "" ? undefined : chosen;
210
+ }
211
+ }
212
+ // 4. Serial port (only for embedded) — the port the board rides on.
213
+ // Detected ports are offered as a choice; the answer seeds test.port in
214
+ // the scaffolded config (no more guessing COM4). "skip" leaves the
215
+ // platform-hint placeholder.
216
+ let serialPort;
217
+ if (!target.isNative) {
218
+ if (partialOptions?.port) {
219
+ serialPort = partialOptions.port;
220
+ console.log(`${chalk.cyan("?")} Serial port: ${chalk.white(serialPort)}`);
221
+ }
222
+ else {
223
+ const detected = detectSerialPorts();
224
+ if (detected.length > 0) {
225
+ const chosenPort = await promptSelect(rl, "Which serial port is the board attached to?", [
226
+ ...detected.map((p) => ({ label: p, value: p })),
227
+ { label: "not listed / set later (type a path)", value: "__other__" },
228
+ { label: "skip — use the placeholder, configure later", value: "" },
229
+ ]);
230
+ serialPort = chosenPort === "" ? undefined
231
+ : chosenPort === "__other__" ? await promptText(rl, "Serial port path", detected[0])
232
+ : chosenPort;
233
+ }
234
+ else {
235
+ const hint = process.platform === "win32" ? "COM4" : "/dev/ttyACM0";
236
+ const typed = await promptText(rl, "Serial port (none detected — plug the board in, or type a path)", hint);
237
+ serialPort = typed === hint ? undefined : typed;
238
+ }
239
+ }
240
+ }
241
+ // 5. Baud rate (only for embedded)
242
+ let baudRate;
243
+ if (!target.isNative) {
244
+ // Zephyr consoles default to 115200; 9600 is the Arduino-class default.
245
+ const defaultBaud = framework === 'zephyr' ? 115200 : 9600;
246
+ if (partialOptions?.baud) {
247
+ baudRate = partialOptions.baud;
248
+ console.log(`${chalk.cyan("?")} Serial baud rate: ${chalk.white(baudRate)}`);
249
+ }
250
+ else {
251
+ const baudAnswer = await promptText(rl, "Serial baud rate", String(defaultBaud));
252
+ baudRate = parseInt(baudAnswer, 10);
253
+ if (Number.isNaN(baudRate) || baudRate <= 0) {
254
+ baudRate = defaultBaud;
255
+ console.log(` ${chalk.dim(`Using default: ${defaultBaud}`)}`);
256
+ }
257
+ }
258
+ }
259
+ // 5. Starter program
260
+ let includeStarter;
261
+ if (partialOptions?.noStarter) {
262
+ includeStarter = false;
263
+ console.log(`${chalk.cyan("?")} Create starter program: ${chalk.dim("no")}`);
264
+ }
265
+ else if (partialOptions?.noStarter === false) {
266
+ includeStarter = true;
267
+ console.log(`${chalk.cyan("?")} Create starter program: ${chalk.green("yes")}`);
268
+ }
269
+ else {
270
+ includeStarter = await promptConfirm(rl, "Create starter program?", true);
271
+ }
272
+ return {
273
+ probeMethod,
274
+ probeMethods,
275
+ projectName,
276
+ targetId: target.id,
277
+ targetDisplayName: target.displayName,
278
+ isNative: target.isNative,
279
+ architecture: target.architecture,
280
+ board: target.board,
281
+ // Pack fact: does the board's devicetree declare an LED? Drives the
282
+ // starter between LED-blink and an I/O skeleton.
283
+ ...(target.board ? { hasLed: Boolean(activeBoardCatalog()[target.board]?.led) } : {}),
284
+ ...(serialPort ? { port: serialPort } : {}),
285
+ frameworkPackage: frameworkPackage ?? '',
286
+ framework: framework ?? '',
287
+ buildTarget,
288
+ ...(profile.toolchainType ? { toolchainType: profile.toolchainType } : {}),
289
+ // soc rides the config only for native projects — a board target's
290
+ // soc derives from the identifier at boardgen time.
291
+ ...(target.board ? {} : { soc: target.soc }),
292
+ ...(zephyrCustomBoard ? { zephyrCustomBoard: true } : {}),
293
+ baudRate,
294
+ includeStarter,
295
+ ...(target.frameworkData
296
+ ? { frameworkData: target.frameworkData }
297
+ : {}),
298
+ };
299
+ }
300
+ catch (err) {
301
+ if (err && typeof err === 'object' && err.code === 'ERR_USE_AFTER_CLOSE') {
302
+ return null;
303
+ }
304
+ throw err;
305
+ }
306
+ finally {
307
+ // The board picker closes rl itself (it hands stdin to the filterable
308
+ // select); a second close is harmless but explicit is cleaner.
309
+ try {
310
+ rl.close();
311
+ }
312
+ catch { /* already closed */ }
313
+ }
314
+ }
@@ -1,4 +1,5 @@
1
1
  import type { BreakpointMap } from './types.js';
2
+ import type { PlatformStrategy } from '../api/shared/platform-strategy.js';
2
3
  export declare function preprocess(options: {
3
4
  /** The source file path (for line number calculation) */
4
5
  fileName: string;
@@ -6,4 +7,8 @@ export declare function preprocess(options: {
6
7
  breakpoints: BreakpointMap;
7
8
  /** The source text to transform */
8
9
  source: string;
10
+ /** The platform strategy that will emit this file — its debug dialect
11
+ * (generateDebug*Code) is used. Omit when unknown; the global loaded
12
+ * framework is consulted, then the generic printf codegen. */
13
+ strategy?: PlatformStrategy;
9
14
  }): string;
@@ -10,20 +10,32 @@ import path from 'node:path';
10
10
  import { getBreakpointsForFile } from './breakpoint-loader.js';
11
11
  import { getLoadedFramework, hasLoadedFramework } from '../framework-registry.js';
12
12
  import { GenericStrategy } from '../platform/generic-strategy.js';
13
- function getDebugStrategy() {
13
+ function getDebugStrategy(explicit) {
14
+ // The strategy that will EMIT this file decides the debug dialect — its
15
+ // codegen must match the runtime the emitted code links against. The
16
+ // global loaded-framework registry is only a fallback for callers that
17
+ // don't know their strategy (picking it blind once emitted Zephyr printk
18
+ // code into a native build).
19
+ if (explicit) {
20
+ if (explicit.generateDebugInitCode)
21
+ return explicit;
22
+ // The emitting strategy has no debug dialect (e.g. NativeStrategy) —
23
+ // its embedded fallback is the generic printf codegen.
24
+ return new GenericStrategy();
25
+ }
14
26
  if (hasLoadedFramework()) {
15
27
  const { strategy } = getLoadedFramework();
16
28
  if (strategy.generateDebugInitCode)
17
29
  return strategy;
18
30
  }
19
- // Fall back to the generic strategy's std::cout-based debug output
31
+ // Fall back to the generic strategy's printf-based debug output
20
32
  return new GenericStrategy();
21
33
  }
22
34
  // ---------------------------------------------------------------------------
23
35
  // Public API
24
36
  // ---------------------------------------------------------------------------
25
37
  export function preprocess(options) {
26
- const { fileName, breakpoints, source } = options;
38
+ const { fileName, breakpoints, source, strategy: emittingStrategy } = options;
27
39
  const breakpointLines = getBreakpointsForFile(breakpoints, fileName);
28
40
  if (breakpointLines.length === 0) {
29
41
  return source; // No breakpoints, return unchanged
@@ -39,7 +51,7 @@ export function preprocess(options) {
39
51
  }
40
52
  // Collect variables in scope at each breakpoint
41
53
  const scopeAnalyzer = new ScopeAnalyzer(sf);
42
- const debugStrategy = getDebugStrategy();
54
+ const debugStrategy = getDebugStrategy(emittingStrategy);
43
55
  // Per-file breakpoint ID counter. Each HALTING breakpoint gets a stable
44
56
  // integer ID so the codegen can emit a per-breakpoint disable flag (skipped
45
57
  // via the 's' key at runtime). IDs are scoped to this file and reset per
@@ -198,7 +210,11 @@ class ScopeAnalyzer {
198
210
  // so a local declared on line N is not yet in scope at line N. Params and
199
211
  // enclosing-scope vars are in scope from the function's first line.
200
212
  const recordFunction = (node, currentVars) => {
201
- const startLine = this.sf.getLineAndCharacterOfPosition(node.getStart()).line;
213
+ // Scope starts at the BODY, not the signature: a breakpoint on the
214
+ // signature line is injected BEFORE the function exists (top-level
215
+ // position), so params are not in scope there — treating it as
216
+ // in-function dumped undeclared identifiers into main().
217
+ const startLine = this.sf.getLineAndCharacterOfPosition((node.body ?? node).getStart()).line;
202
218
  const endLine = node.body
203
219
  ? this.sf.getLineAndCharacterOfPosition(node.body.getEnd()).line
204
220
  : startLine;
@@ -227,9 +243,17 @@ class ScopeAnalyzer {
227
243
  this.functionVars.set(line, [...alwaysInScope, ...localsInScope]);
228
244
  }
229
245
  };
230
- // Find top-level functions, then recurse into their bodies for nested ones.
246
+ // Find top-level functions, then recurse into their bodies for nested
247
+ // ones. Top-level functions start from an EMPTY enclosing scope: the
248
+ // module-scope variables live in the enclosing scope chain only for
249
+ // NESTED functions. Top-level statements lower into main() locals, so a
250
+ // module-scope name is not visible inside a top-level function — dumping
251
+ // one there emitted references to undeclared identifiers (a breakpoint in
252
+ // any function flagged every top-level const). A breakpoint placed on a
253
+ // top-level statement still sees them via getVariablesInScope's top-level
254
+ // path, filtered by declaration order.
231
255
  for (const stmt of this.sf.statements) {
232
- this.findAndRecordFunctions(stmt, topLevelVars, recordFunction);
256
+ this.findAndRecordFunctions(stmt, [], recordFunction);
233
257
  }
234
258
  }
235
259
  /** Collect variables declared directly by a single statement (top-level use). */
@@ -15,7 +15,7 @@ export interface DiagnosticsReportInput {
15
15
  /** Target architecture (e.g. "avr", "esp32") */
16
16
  target: string;
17
17
  /** Board package name */
18
- boardPackage?: string;
18
+ boardTarget?: string;
19
19
  /** Framework package name */
20
20
  frameworkPackage?: string;
21
21
  /** Build target identifier */
@@ -102,7 +102,7 @@ function buildPinUsage(program, boardPinsData) {
102
102
  const usage = program.peripheralUsage;
103
103
  // Map board pins to GPIO entries
104
104
  for (const pin of boardPinsData) {
105
- // Only include pins that are explicitly used in the sketch to keep the table clean
105
+ // Only include pins that are explicitly used in the program to keep the table clean
106
106
  const isExplicitlyUsed = usage.pinsUsed.has(pin.name) ||
107
107
  pin.aliases.some(a => usage.pinsUsed.has(a)) ||
108
108
  (pin.number !== undefined && (usage.outputPins.has(pin.number) ||
@@ -161,14 +161,6 @@ function buildPinUsage(program, boardPinsData) {
161
161
  pins: [...usage.pwmPinsUsed].map((n) => `D${n}`),
162
162
  });
163
163
  }
164
- if (usage.timer0) {
165
- peripherals.push({
166
- type: "timer",
167
- instance: 0,
168
- displayName: "Timer0 (millis/micros)",
169
- pins: [],
170
- });
171
- }
172
164
  if (usage.externalInterrupts) {
173
165
  peripherals.push({
174
166
  type: "interrupt",
@@ -261,10 +253,10 @@ export function buildDiagnosticsReport(input) {
261
253
  timestamp: new Date().toISOString(),
262
254
  sourceFile: path.basename(input.entryFile),
263
255
  target: input.target,
264
- board: input.boardPackage,
256
+ board: input.boardTarget,
265
257
  framework: input.frameworkPackage,
266
258
  buildTarget: input.buildTarget,
267
- outputFile: input.outputFile ?? path.basename(input.entryFile, path.extname(input.entryFile)) + ".ino",
259
+ outputFile: input.outputFile ?? path.basename(input.entryFile, path.extname(input.entryFile)) + ".cpp",
268
260
  boardDetails: program?.boardConstants ? {
269
261
  mcu: program.boardConstants.get("mcu"),
270
262
  architecture: program.boardConstants.get("architecture"),
@@ -8,7 +8,7 @@ export interface GpioPinEntry {
8
8
  peripheralRole?: string;
9
9
  }
10
10
  export interface PeripheralAllocation {
11
- type: "i2c" | "spi" | "uart" | "adc" | "pwm" | "timer" | "interrupt";
11
+ type: "i2c" | "spi" | "uart" | "adc" | "pwm" | "interrupt";
12
12
  instance: number;
13
13
  displayName: string;
14
14
  pins: string[];
@@ -55,7 +55,7 @@ function buildExecutionFlowSection(flow, callGraph) {
55
55
  const lines = [];
56
56
  lines.push("## Execution Flow");
57
57
  lines.push("");
58
- lines.push("> This graph shows the relationships between entry points, HAL objects, and Arduino APIs.");
58
+ lines.push("> This graph shows the relationships between entry points, HAL objects, and framework APIs.");
59
59
  lines.push("");
60
60
  lines.push(buildExecutionFlowDiagram(flow, callGraph));
61
61
  lines.push("");
@@ -174,7 +174,7 @@ function buildAsyncTasksSection(tasks) {
174
174
  lines.push("## Cooperative Multitasking");
175
175
  lines.push("");
176
176
  if (tasks.length === 0) {
177
- lines.push("> _No async tasks registered in this sketch._");
177
+ lines.push("> _No async tasks registered in this program._");
178
178
  lines.push("");
179
179
  return lines.join("\n");
180
180
  }
@@ -1,7 +1,7 @@
1
1
  import type { CallGraph } from "../ir/call-graph.js";
2
2
  import type { GpioPinEntry, PeripheralAllocation, AsyncTaskEntry, ExecutionFlow, HeapEstimate, PeripheralConflictEntry, ModuleGraph, TreeShakingReport, BuildTiming } from "./json-schema.js";
3
3
  /**
4
- * Build a categorized architecture diagram showing how the sketch
4
+ * Build a categorized architecture diagram showing how the program
5
5
  * is organized: entry points, HAL objects, HAL APIs (grouped by
6
6
  * subsystem), state variables, and custom functions.
7
7
  *
@@ -73,7 +73,7 @@ function isHalApi(name) {
73
73
  }
74
74
  // ── Execution Flow Diagram ──────────────────────────────────────────────────
75
75
  /**
76
- * Build a categorized architecture diagram showing how the sketch
76
+ * Build a categorized architecture diagram showing how the program
77
77
  * is organized: entry points, HAL objects, HAL APIs (grouped by
78
78
  * subsystem), state variables, and custom functions.
79
79
  *
@@ -1,5 +1,5 @@
1
1
  import { buildEmitterContext } from "./emitters/setup.js";
2
- import { emitPreamble, emitAsyncTaskClasses, finalizeOutput } from "./emitters/output-finalizer.js";
2
+ import { emitPreamble, emitAsyncTaskClasses, emitAsyncMethodTasks, finalizeOutput } from "./emitters/output-finalizer.js";
3
3
  import { runTopLevelPreprocessing } from "./emitters/top-level-prep.js";
4
4
  import { synthesizeEntrypoints } from "./emitters/entrypoint-synthesizer.js";
5
5
  import { emitTypeDeclarations } from "./emitters/type-decl-emitter.js";
@@ -39,6 +39,9 @@ export function emitCpp(program, options) {
39
39
  emitFunctionForwardDeclarations(ctx);
40
40
  // 7. Emit classes
41
41
  emitClasses(ctx);
42
+ // 7.5. Async-method tasks — after the owning classes (segments dereference
43
+ // _owner->field on a complete type), before functions (loop pump + starters).
44
+ emitAsyncMethodTasks(ctx);
42
45
  // 8. Emit post-class declarations (runtime vars, promoted vars, object literal structs)
43
46
  emitPostClassDeclarations(ctx);
44
47
  // 9. Emit callbacks + forward declarations + function definitions
@@ -142,7 +142,7 @@ export function emitClasses(ctx) {
142
142
  // Build variable → accessor map. NOTE: the variable/parameter registration
143
143
  // now lives in setup.ts so it runs before any emit pass. The classAccessorNames
144
144
  // map built here is still used below for the per-method "this" registration.
145
- // See SUPPORT_MATRIX §4.3 (demo #4 fix).
145
+ // (demo #4 fix.)
146
146
  // Build virtual/override maps for inherited methods
147
147
  const classMethodNames = new Map();
148
148
  for (const cls of program.classes) {
@@ -331,7 +331,19 @@ export function emitClasses(ctx) {
331
331
  if (method.typeParameters && method.typeParameters.length > 0) {
332
332
  appendSourceLine(ctx, ` template<typename ${method.typeParameters.join(", typename ")}>`);
333
333
  }
334
- appendSourceLine(ctx, ` ${virtualPrefix}${staticPrefix}${returnType} ${escapeCppKeyword(method.name, reservedNames)}(${methodParams})${overrideSuffix} {`);
334
+ // Async methods: the body becomes an owner-bound cooperative task
335
+ // (setup.ts generates it); the in-class body just binds the receiver
336
+ // via the forward-declared starter. Return type is void — the task
337
+ // is fire-and-forget.
338
+ const asyncKick = method.isAsync && ctx.hasAsyncRuntime;
339
+ const emittedReturnType = asyncKick ? "void" : returnType;
340
+ appendSourceLine(ctx, ` ${virtualPrefix}${staticPrefix}${emittedReturnType} ${escapeCppKeyword(method.name, reservedNames)}(${methodParams})${overrideSuffix} {`);
341
+ if (asyncKick) {
342
+ appendSourceLine(ctx, ` __tc_async_start_${classDef.name}_${method.name}(this);`);
343
+ appendSourceLine(ctx, " }");
344
+ appendSourceLine(ctx, "");
345
+ continue;
346
+ }
335
347
  const methodScope = createChildEmissionScope(topLevelScope, method.parameters);
336
348
  addClassFieldsToScope(classDef, methodScope);
337
349
  const classAccessors = classAccessorNames.get(classDef.name);
@@ -403,7 +415,15 @@ export function emitClasses(ctx) {
403
415
  if (method.typeParameters && method.typeParameters.length > 0) {
404
416
  appendSourceLine(ctx, ` template<typename ${method.typeParameters.join(", typename ")}>`);
405
417
  }
406
- appendSourceLine(ctx, ` ${virtualPrefix}${staticPrefix}${normalizeCppTypeForTarget(method.returnType)} ${escapeCppKeyword(method.name, reservedNames)}(${methodParams})${overrideSuffix} {`);
418
+ const asyncKick = method.isAsync && ctx.hasAsyncRuntime;
419
+ const emittedReturnType = asyncKick ? "void" : normalizeCppTypeForTarget(method.returnType);
420
+ appendSourceLine(ctx, ` ${virtualPrefix}${staticPrefix}${emittedReturnType} ${escapeCppKeyword(method.name, reservedNames)}(${methodParams})${overrideSuffix} {`);
421
+ if (asyncKick) {
422
+ appendSourceLine(ctx, ` __tc_async_start_${classDef.name}_${method.name}(this);`);
423
+ appendSourceLine(ctx, " }");
424
+ appendSourceLine(ctx, "");
425
+ continue;
426
+ }
407
427
  const methodScope = createChildEmissionScope(topLevelScope, method.parameters);
408
428
  addClassFieldsToScope(classDef, methodScope);
409
429
  withThisAccessors(classDef, method.isStatic, () => {
@@ -469,7 +489,15 @@ export function emitClasses(ctx) {
469
489
  if (method.typeParameters && method.typeParameters.length > 0) {
470
490
  appendSourceLine(ctx, ` template<typename ${method.typeParameters.join(", typename ")}>`);
471
491
  }
472
- appendSourceLine(ctx, ` ${virtualPrefix}${staticPrefix}${normalizeCppTypeForTarget(method.returnType)} ${escapeCppKeyword(method.name, reservedNames)}(${methodParams})${overrideSuffix} {`);
492
+ const asyncKick = method.isAsync && ctx.hasAsyncRuntime;
493
+ const emittedReturnType = asyncKick ? "void" : normalizeCppTypeForTarget(method.returnType);
494
+ appendSourceLine(ctx, ` ${virtualPrefix}${staticPrefix}${emittedReturnType} ${escapeCppKeyword(method.name, reservedNames)}(${methodParams})${overrideSuffix} {`);
495
+ if (asyncKick) {
496
+ appendSourceLine(ctx, ` __tc_async_start_${classDef.name}_${method.name}(this);`);
497
+ appendSourceLine(ctx, " }");
498
+ appendSourceLine(ctx, "");
499
+ continue;
500
+ }
473
501
  const methodScope = createChildEmissionScope(topLevelScope, method.parameters);
474
502
  addClassFieldsToScope(classDef, methodScope);
475
503
  withThisAccessors(classDef, method.isStatic, () => {
@@ -90,6 +90,16 @@ export interface AsyncTaskClass {
90
90
  classDef: string;
91
91
  instanceDecl: string;
92
92
  taskVarName: string;
93
+ /** Async methods: the starter-function DEFINITION (binds the owner and
94
+ * arms the task). Emitted after the task class; the matching prototype
95
+ * (asyncMethodStarters) precedes the owning class. */
96
+ starterDef?: string;
97
+ }
98
+ /** Forward prototypes for async-method starters — emitted before the class
99
+ * definitions whose method bodies call them. */
100
+ export interface AsyncMethodStarter {
101
+ /** The `void __tc_async_start_<Class>_<method>(<Class>*);` prototype. */
102
+ proto: string;
93
103
  }
94
104
  export interface FixPointerFieldAccessFn {
95
105
  (callee: string): string;
@@ -139,6 +149,7 @@ export interface EmitterContext {
139
149
  boardConstants?: BoardConstants;
140
150
  restParamFunctions?: Map<string, string>;
141
151
  asyncTaskClasses: AsyncTaskClass[];
152
+ asyncMethodStarters: AsyncMethodStarter[];
142
153
  asyncFunctionOriginalNames: Set<string>;
143
154
  asyncFunctionMappedNames: Set<string>;
144
155
  hasAsyncRuntime: boolean;
@@ -53,7 +53,7 @@ export function synthesizeEntrypoints(ctx) {
53
53
  ctx.emitDiagnostics.push({
54
54
  severity: "error",
55
55
  code: "gpio-unsupported-on-target",
56
- message: `${which} require GPIO hardware, which this target (${ctx.options.target}) does not model. Use the browser preview to exercise pin-driven UI, or target @typecad/framework-arduino.`,
56
+ message: `${which} require GPIO hardware, which this target (${ctx.options.target}) does not model. Use the browser preview to exercise pin-driven UI, or target a framework that models GPIO pins.`,
57
57
  source: program.fileName,
58
58
  });
59
59
  }