@typecad/cuttlefish 0.1.0-alpha.2 → 1.0.0-alpha.10

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 (222) hide show
  1. package/README.md +10 -10
  2. package/dist/api/board-types.d.ts +1 -1
  3. package/dist/api/config.d.ts +32 -0
  4. package/dist/api/index.d.ts +1 -1
  5. package/dist/api/schema/types.d.ts +15 -0
  6. package/dist/api/shared/async-runtime-static.d.ts +12 -1
  7. package/dist/api/shared/async-runtime-static.js +69 -24
  8. package/dist/api/shared/async-symbol-detector.d.ts +11 -0
  9. package/dist/api/shared/async-symbol-detector.js +140 -0
  10. package/dist/api/shared/async-types.d.ts +24 -0
  11. package/dist/api/shared/coop-scheduler.d.ts +60 -0
  12. package/dist/api/shared/coop-scheduler.js +149 -0
  13. package/dist/api/shared/display-adapter.d.ts +4 -3
  14. package/dist/api/shared/display-adapter.js +15 -118
  15. package/dist/api/shared/display-adapters/sdl.js +38 -31
  16. package/dist/api/shared/display-profile.d.ts +47 -25
  17. package/dist/api/shared/display-profile.js +31 -164
  18. package/dist/api/shared/framework-manifest-registry.d.ts +9 -0
  19. package/dist/api/shared/framework-manifest-registry.js +27 -0
  20. package/dist/api/shared/framework-manifest.d.ts +595 -0
  21. package/dist/api/shared/framework-manifest.js +193 -0
  22. package/dist/api/shared/glcdfont.d.ts +12 -0
  23. package/dist/api/shared/glcdfont.js +124 -0
  24. package/dist/api/shared/graphics-strategy.d.ts +37 -0
  25. package/dist/api/shared/hal-op-ir.d.ts +764 -1
  26. package/dist/api/shared/hal-op-ir.js +129 -1
  27. package/dist/api/shared/index.d.ts +20 -2
  28. package/dist/api/shared/index.js +25 -1
  29. package/dist/api/shared/ir-core.d.ts +4 -0
  30. package/dist/api/shared/ir-declarations.d.ts +6 -0
  31. package/dist/api/shared/native-display-op-resolver.d.ts +10 -0
  32. package/dist/api/shared/native-display-op-resolver.js +64 -0
  33. package/dist/api/shared/platform-strategy.d.ts +119 -2
  34. package/dist/api/shared/polyfill-helper-registry.js +18 -1
  35. package/dist/api/shared/promise-runtime.d.ts +1 -1
  36. package/dist/api/shared/promise-runtime.js +95 -13
  37. package/dist/api/shared/toolchain-types.d.ts +17 -0
  38. package/dist/api/shared/types.d.ts +8 -0
  39. package/dist/api/shared/validate-framework-manifest.d.ts +28 -0
  40. package/dist/api/shared/validate-framework-manifest.js +494 -0
  41. package/dist/api/shared/worker-runtime-polyfill.d.ts +22 -0
  42. package/dist/api/shared/worker-runtime-polyfill.js +34 -0
  43. package/dist/api/shared/worker-runtime.d.ts +69 -0
  44. package/dist/api/shared/worker-runtime.js +163 -0
  45. package/dist/cli-utils.d.ts +1 -0
  46. package/dist/cli-utils.js +3 -1
  47. package/dist/cli.js +249 -32
  48. package/dist/config-loader.d.ts +7 -2
  49. package/dist/config-loader.js +23 -3
  50. package/dist/config-schema.d.ts +112 -70
  51. package/dist/config-schema.js +14 -0
  52. package/dist/contract/board-generator.d.ts +17 -0
  53. package/dist/contract/board-generator.js +57 -0
  54. package/dist/contract/contract-parser.d.ts +217 -0
  55. package/dist/contract/contract-parser.js +224 -0
  56. package/dist/contract/index.d.ts +22 -0
  57. package/dist/contract/index.js +68 -0
  58. package/dist/create/board-codegen.js +4 -4
  59. package/dist/create/board-generators.js +4 -5
  60. package/dist/create/board-spec.d.ts +72 -75
  61. package/dist/create/board-spec.js +0 -1
  62. package/dist/create/framework-catalog.d.ts +64 -0
  63. package/dist/create/framework-catalog.js +136 -0
  64. package/dist/create/index.d.ts +5 -1
  65. package/dist/create/index.js +3 -1
  66. package/dist/create/init-scaffold.d.ts +12 -3
  67. package/dist/create/init-scaffold.js +49 -14
  68. package/dist/create/init-templates.d.ts +4 -0
  69. package/dist/create/init-templates.js +263 -17
  70. package/dist/create/init-wizard.js +41 -27
  71. package/dist/create/install-deps.d.ts +32 -0
  72. package/dist/create/install-deps.js +46 -0
  73. package/dist/debug/preprocessor.js +178 -39
  74. package/dist/debug/types.d.ts +23 -0
  75. package/dist/diagnostics/mermaid-builder.d.ts +1 -1
  76. package/dist/diagnostics/mermaid-builder.js +34 -24
  77. package/dist/emit/compliance/arxml-writer.d.ts +11 -0
  78. package/dist/emit/compliance/arxml-writer.js +34 -0
  79. package/dist/emit/compliance/compliance-context.d.ts +57 -0
  80. package/dist/emit/compliance/compliance-context.js +113 -0
  81. package/dist/emit/compliance/deviation-ledger.d.ts +27 -0
  82. package/dist/emit/compliance/deviation-ledger.js +47 -0
  83. package/dist/emit/compliance/deviation-writer.d.ts +30 -0
  84. package/dist/emit/compliance/deviation-writer.js +37 -0
  85. package/dist/emit/compliance/index.d.ts +7 -0
  86. package/dist/emit/compliance/index.js +6 -0
  87. package/dist/emit/compliance/rule-engine.d.ts +13 -0
  88. package/dist/emit/compliance/rule-engine.js +101 -0
  89. package/dist/emit/compliance/rules.d.ts +16 -0
  90. package/dist/emit/compliance/rules.js +191 -0
  91. package/dist/emit/compliance/types.d.ts +59 -0
  92. package/dist/emit/compliance/types.js +8 -0
  93. package/dist/emit/cpp-emitter.js +4 -3
  94. package/dist/emit/emitters/class-emitter.js +6 -1
  95. package/dist/emit/emitters/emitter-context.d.ts +28 -1
  96. package/dist/emit/emitters/function-emitter-impl.js +153 -53
  97. package/dist/emit/emitters/line-appender.js +19 -0
  98. package/dist/emit/emitters/line-marker.d.ts +38 -0
  99. package/dist/emit/emitters/line-marker.js +39 -0
  100. package/dist/emit/emitters/output-finalizer.d.ts +6 -0
  101. package/dist/emit/emitters/output-finalizer.js +101 -12
  102. package/dist/emit/emitters/setup.d.ts +17 -0
  103. package/dist/emit/emitters/setup.js +372 -30
  104. package/dist/emit/emitters/top-level-prep.js +8 -0
  105. package/dist/emit/emitters/type-decl-emitter.js +28 -3
  106. package/dist/emit/emitters/ui-emitter.js +33 -9
  107. package/dist/emit/expression-renderer.d.ts +1 -1
  108. package/dist/emit/expression-renderer.js +91 -6
  109. package/dist/emit/route-hal-op.js +18 -5
  110. package/dist/emit/snprintf-helpers.js +15 -4
  111. package/dist/emit/statement-renderer.d.ts +10 -0
  112. package/dist/emit/statement-renderer.js +34 -5
  113. package/dist/emit/utils/async-state-machine.js +221 -125
  114. package/dist/emit/utils/hal-op-cpp-type.d.ts +6 -0
  115. package/dist/emit/utils/hal-op-cpp-type.js +40 -0
  116. package/dist/framework-package.js +2 -0
  117. package/dist/framework-registry.d.ts +17 -0
  118. package/dist/install/framework-catalog.d.ts +53 -0
  119. package/dist/install/framework-catalog.js +107 -0
  120. package/dist/install/handle-install.d.ts +35 -0
  121. package/dist/install/handle-install.js +177 -0
  122. package/dist/install/index.d.ts +4 -0
  123. package/dist/install/index.js +3 -0
  124. package/dist/ir/adc-range-validation.js +40 -26
  125. package/dist/ir/build-ir-state.d.ts +1 -0
  126. package/dist/ir/build-ir-state.js +14 -0
  127. package/dist/ir/build-ir.js +9 -5
  128. package/dist/ir/call-graph.js +16 -0
  129. package/dist/ir/expression-to-ir.js +207 -6
  130. package/dist/ir/feature-registry.js +7 -25
  131. package/dist/ir/function-builder.js +22 -0
  132. package/dist/ir/hal/hal-emitter.d.ts +5 -2
  133. package/dist/ir/hal/hal-emitter.js +50 -18
  134. package/dist/ir/hal/hal-parser.d.ts +6 -0
  135. package/dist/ir/hal/hal-parser.js +81 -0
  136. package/dist/ir/hal/hal-plugins.js +733 -1
  137. package/dist/ir/identifier-collector.js +35 -0
  138. package/dist/ir/interrupt-analysis.d.ts +5 -1
  139. package/dist/ir/interrupt-analysis.js +16 -19
  140. package/dist/ir/memory-budget-validation.js +1 -0
  141. package/dist/ir/network-validation.d.ts +4 -0
  142. package/dist/ir/network-validation.js +184 -0
  143. package/dist/ir/ownership-analysis.js +39 -1
  144. package/dist/ir/peripheral-ownership.js +5 -0
  145. package/dist/ir/peripheral-validation.d.ts +1 -1
  146. package/dist/ir/peripheral-validation.js +6 -3
  147. package/dist/ir/pin-alias-conflict.d.ts +1 -1
  148. package/dist/ir/pin-alias-conflict.js +2 -1
  149. package/dist/ir/pin-capability-validation.js +71 -32
  150. package/dist/ir/pin-mode-validation.d.ts +2 -2
  151. package/dist/ir/pin-mode-validation.js +54 -18
  152. package/dist/ir/pin-safety.d.ts +1 -1
  153. package/dist/ir/pin-safety.js +2 -1
  154. package/dist/ir/program-analysis.d.ts +81 -0
  155. package/dist/ir/program-analysis.js +429 -0
  156. package/dist/ir/pulldown-validation.d.ts +1 -1
  157. package/dist/ir/pulldown-validation.js +2 -1
  158. package/dist/ir/pwm-timer-sharing.d.ts +1 -1
  159. package/dist/ir/pwm-timer-sharing.js +2 -1
  160. package/dist/ir/render-expr.js +11 -0
  161. package/dist/ir/resource-analysis.js +2 -0
  162. package/dist/ir/timer0-pwm-timing-conflict.d.ts +1 -1
  163. package/dist/ir/timer0-pwm-timing-conflict.js +2 -1
  164. package/dist/ir/timing-validation.d.ts +6 -1
  165. package/dist/ir/timing-validation.js +61 -13
  166. package/dist/ir/transformers/call-statement.js +105 -0
  167. package/dist/ir/transformers/expressions.js +62 -0
  168. package/dist/ir/transformers/hal-call-resolver.js +21 -0
  169. package/dist/ir/transformers/hal-emit-helpers.js +1 -1
  170. package/dist/ir/transformers/namespace-methods.js +17 -12
  171. package/dist/ir/transformers/ui-reactive.js +2 -2
  172. package/dist/ir/transformers/variables.js +137 -19
  173. package/dist/ir/try-catch-validation.js +2 -0
  174. package/dist/ir/type-resolution.js +20 -2
  175. package/dist/ir/ui-element-auto-wire.js +7 -5
  176. package/dist/ir/unit-suspicion-validation.js +9 -7
  177. package/dist/ir/utils/map-statements.d.ts +4 -0
  178. package/dist/ir/utils/map-statements.js +79 -0
  179. package/dist/ir/validation-orchestrator.js +15 -8
  180. package/dist/ir/worker-analysis.d.ts +10 -0
  181. package/dist/ir/worker-analysis.js +261 -0
  182. package/dist/libdef/c-to-decl.d.ts +27 -0
  183. package/dist/libdef/c-to-decl.js +397 -0
  184. package/dist/libdef/component-decls.d.ts +2 -0
  185. package/dist/libdef/component-decls.js +6 -0
  186. package/dist/libdef/component-discovery.d.ts +43 -0
  187. package/dist/libdef/component-discovery.js +83 -0
  188. package/dist/libdef/cpp-to-decl.d.ts +9 -0
  189. package/dist/libdef/cpp-to-decl.js +72 -0
  190. package/dist/libdef/registry.js +5 -2
  191. package/dist/lint-cache.d.ts +59 -0
  192. package/dist/lint-cache.js +257 -0
  193. package/dist/orchestrator/graph-builder.js +14 -9
  194. package/dist/platform/coop-scheduler-runtime.d.ts +19 -0
  195. package/dist/platform/coop-scheduler-runtime.js +41 -0
  196. package/dist/platform/generic-strategy.d.ts +15 -3
  197. package/dist/platform/generic-strategy.js +49 -4
  198. package/dist/safety/safety-bridge.d.ts +11 -0
  199. package/dist/safety/safety-bridge.js +48 -0
  200. package/dist/safety/sidecar-bridge.d.ts +5 -0
  201. package/dist/safety/sidecar-bridge.js +22 -0
  202. package/dist/safety-hook.d.ts +79 -0
  203. package/dist/safety-hook.js +35 -0
  204. package/dist/testing.d.ts +8 -2
  205. package/dist/testing.js +9 -2
  206. package/dist/transpile.d.ts +3 -0
  207. package/dist/transpile.js +191 -49
  208. package/dist/types.d.ts +22 -3
  209. package/dist/ui-hook.d.ts +17 -3
  210. package/dist/utils/cli.js +122 -2
  211. package/dist/utils/fs.d.ts +13 -0
  212. package/dist/utils/fs.js +50 -0
  213. package/dist/utils/ui.js +3 -1
  214. package/package.json +23 -3
  215. package/dist/api/shared/display-adapters/eink-mono.d.ts +0 -2
  216. package/dist/api/shared/display-adapters/eink-mono.js +0 -53
  217. package/dist/api/shared/display-adapters/ssd1309.d.ts +0 -2
  218. package/dist/api/shared/display-adapters/ssd1309.js +0 -136
  219. package/dist/api/shared/display-adapters/st7796.d.ts +0 -2
  220. package/dist/api/shared/display-adapters/st7796.js +0 -132
  221. package/dist/ir/heap-array-validation.d.ts +0 -24
  222. package/dist/ir/heap-array-validation.js +0 -29
@@ -1,5 +1,5 @@
1
1
  import { findBoardPinByName } from './board-pin-utils.js';
2
- export function validatePinAliasConflicts(usage, boardConstants) {
2
+ export function validatePinAliasConflicts(usage, boardConstants, filePath) {
3
3
  if (!boardConstants || usage.pinsUsed.size === 0) {
4
4
  return [];
5
5
  }
@@ -26,6 +26,7 @@ export function validatePinAliasConflicts(usage, boardConstants) {
26
26
  diagnostics.push({
27
27
  severity: 'warning',
28
28
  code: 'pin-alias-conflict',
29
+ filePath,
29
30
  source: 'pin-alias-conflict',
30
31
  message: `Pin '${canonicalPinName}' is referenced through multiple names${boardLabel}: ${orderedNames.join(', ')}. These names refer to the same physical pin. Pick one name to keep pin usage and diagnostics unambiguous.`,
31
32
  });
@@ -77,10 +77,39 @@ function findPinsWithCapability(capability, boardConstants) {
77
77
  }
78
78
  return result;
79
79
  }
80
+ /**
81
+ * Does the board declare ANY per-pin `type:'pwm'` function entry anywhere?
82
+ *
83
+ * AVR-family boards (atmega328p, etc.) hardwire PWM to specific timer-output
84
+ * pins, so their MCU descriptors list `{ type:'pwm', ... }` per pin and the
85
+ * capability check correctly rejects PWM on non-timer pins. ESP32-family
86
+ * boards route LEDC to ANY output GPIO via the GPIO matrix, so their MCU
87
+ * descriptors carry zero `type:'pwm'` entries — PWM is a board-wide property
88
+ * (the `pins.pwm` collection), not a per-pin function. On such boards the
89
+ * per-pin capability check would false-positive on every pin, so the caller
90
+ * skips it when this returns false. This distinguishes the two families
91
+ * cleanly (AVR has entries, ESP32 has none) without special-casing arch names.
92
+ */
93
+ function boardHasAnyPwmFunctionEntries(boardConstants) {
94
+ if (!boardConstants)
95
+ return false;
96
+ for (let i = 0; i < 100; i++) {
97
+ if (boardConstants.get(`pins.all.${i}.name`) === undefined)
98
+ continue;
99
+ for (let j = 0; j < 10; j++) {
100
+ const type = boardConstants.get(`pins.all.${i}.functions.${j}.type`);
101
+ if (type === undefined)
102
+ break;
103
+ if (String(type) === 'pwm')
104
+ return true;
105
+ }
106
+ }
107
+ return false;
108
+ }
80
109
  // ---------------------------------------------------------------------------
81
110
  // Capability check for a single HAL operation
82
111
  // ---------------------------------------------------------------------------
83
- function checkCapability(operation, sourceLine, sourceCol, boardConstants, diagnostics) {
112
+ function checkCapability(operation, sourceLine, sourceCol, filePath, boardConstants, diagnostics) {
84
113
  // Only operations with a pin field are capability-checked.
85
114
  const op = operation.operation;
86
115
  if (!('pin' in operation))
@@ -88,6 +117,13 @@ function checkCapability(operation, sourceLine, sourceCol, boardConstants, diagn
88
117
  const capability = operationToCapability(op);
89
118
  if (!capability)
90
119
  return; // GPIO/timing — always available.
120
+ // ESP32-family boards route PWM (LEDC) to any output GPIO via the GPIO
121
+ // matrix, so they carry zero per-pin `type:'pwm'` entries (unlike AVR,
122
+ // which hardwires PWM to timer-output pins). On such boards the per-pin PWM
123
+ // capability check would reject every pin; skip it and let the framework's
124
+ // LEDC driver handle routing. This does NOT weaken AVR, which has entries.
125
+ if (capability === 'pwm' && !boardHasAnyPwmFunctionEntries(boardConstants))
126
+ return;
91
127
  const pin = operation.pin;
92
128
  if (typeof pin !== 'number' || pin < 0)
93
129
  return;
@@ -145,48 +181,49 @@ function checkCapability(operation, sourceLine, sourceCol, boardConstants, diagn
145
181
  hint,
146
182
  line: sourceLine,
147
183
  column: sourceCol,
184
+ filePath,
148
185
  source: 'pin-capability-validation',
149
186
  });
150
187
  }
151
188
  // ---------------------------------------------------------------------------
152
189
  // IR tree scanner
153
190
  // ---------------------------------------------------------------------------
154
- function scanExpression(expr, boardConstants, parentLine, parentCol, diagnostics) {
191
+ function scanExpression(expr, boardConstants, parentLine, parentCol, parentFilePath, diagnostics) {
155
192
  if (!expr || typeof expr !== 'object')
156
193
  return;
157
194
  switch (expr.kind) {
158
195
  case 'binary': {
159
- scanExpression(expr.left, boardConstants, parentLine, parentCol, diagnostics);
160
- scanExpression(expr.right, boardConstants, parentLine, parentCol, diagnostics);
196
+ scanExpression(expr.left, boardConstants, parentLine, parentCol, parentFilePath, diagnostics);
197
+ scanExpression(expr.right, boardConstants, parentLine, parentCol, parentFilePath, diagnostics);
161
198
  break;
162
199
  }
163
200
  case 'ternary': {
164
- scanExpression(expr.condition, boardConstants, parentLine, parentCol, diagnostics);
165
- scanExpression(expr.whenTrue, boardConstants, parentLine, parentCol, diagnostics);
166
- scanExpression(expr.whenFalse, boardConstants, parentLine, parentCol, diagnostics);
201
+ scanExpression(expr.condition, boardConstants, parentLine, parentCol, parentFilePath, diagnostics);
202
+ scanExpression(expr.whenTrue, boardConstants, parentLine, parentCol, parentFilePath, diagnostics);
203
+ scanExpression(expr.whenFalse, boardConstants, parentLine, parentCol, parentFilePath, diagnostics);
167
204
  break;
168
205
  }
169
206
  case 'property-access': {
170
- scanExpression(expr.object, boardConstants, parentLine, parentCol, diagnostics);
207
+ scanExpression(expr.object, boardConstants, parentLine, parentCol, parentFilePath, diagnostics);
171
208
  break;
172
209
  }
173
210
  case 'unary': {
174
- scanExpression(expr.operand, boardConstants, parentLine, parentCol, diagnostics);
211
+ scanExpression(expr.operand, boardConstants, parentLine, parentCol, parentFilePath, diagnostics);
175
212
  break;
176
213
  }
177
214
  case 'paren': {
178
- scanExpression(expr.inner, boardConstants, parentLine, parentCol, diagnostics);
215
+ scanExpression(expr.inner, boardConstants, parentLine, parentCol, parentFilePath, diagnostics);
179
216
  break;
180
217
  }
181
218
  case 'array': {
182
219
  for (const el of expr.elements) {
183
- scanExpression(el, boardConstants, parentLine, parentCol, diagnostics);
220
+ scanExpression(el, boardConstants, parentLine, parentCol, parentFilePath, diagnostics);
184
221
  }
185
222
  break;
186
223
  }
187
224
  case 'object': {
188
225
  for (const field of expr.fields) {
189
- scanExpression(field.value, boardConstants, parentLine, parentCol, diagnostics);
226
+ scanExpression(field.value, boardConstants, parentLine, parentCol, parentFilePath, diagnostics);
190
227
  }
191
228
  break;
192
229
  }
@@ -204,43 +241,43 @@ function scanExpression(expr, boardConstants, parentLine, parentCol, diagnostics
204
241
  }
205
242
  case 'method-call': {
206
243
  for (const arg of expr.args) {
207
- scanExpression(arg, boardConstants, parentLine, parentCol, diagnostics);
244
+ scanExpression(arg, boardConstants, parentLine, parentCol, parentFilePath, diagnostics);
208
245
  }
209
246
  break;
210
247
  }
211
248
  case 'element-access': {
212
- scanExpression(expr.object, boardConstants, parentLine, parentCol, diagnostics);
213
- scanExpression(expr.index, boardConstants, parentLine, parentCol, diagnostics);
249
+ scanExpression(expr.object, boardConstants, parentLine, parentCol, parentFilePath, diagnostics);
250
+ scanExpression(expr.index, boardConstants, parentLine, parentCol, parentFilePath, diagnostics);
214
251
  break;
215
252
  }
216
253
  case 'string_concat': {
217
254
  for (const part of expr.parts) {
218
- scanExpression(part, boardConstants, parentLine, parentCol, diagnostics);
255
+ scanExpression(part, boardConstants, parentLine, parentCol, parentFilePath, diagnostics);
219
256
  }
220
257
  break;
221
258
  }
222
259
  case 'template_string': {
223
- scanExpression(expr.expression, boardConstants, parentLine, parentCol, diagnostics);
260
+ scanExpression(expr.expression, boardConstants, parentLine, parentCol, parentFilePath, diagnostics);
224
261
  break;
225
262
  }
226
263
  case 'spread_array': {
227
- scanExpression(expr.spreadExpr, boardConstants, parentLine, parentCol, diagnostics);
264
+ scanExpression(expr.spreadExpr, boardConstants, parentLine, parentCol, parentFilePath, diagnostics);
228
265
  for (const el of expr.additionalElements) {
229
- scanExpression(el, boardConstants, parentLine, parentCol, diagnostics);
266
+ scanExpression(el, boardConstants, parentLine, parentCol, parentFilePath, diagnostics);
230
267
  }
231
268
  break;
232
269
  }
233
270
  case 'instanceof': {
234
- scanExpression(expr.object, boardConstants, parentLine, parentCol, diagnostics);
271
+ scanExpression(expr.object, boardConstants, parentLine, parentCol, parentFilePath, diagnostics);
235
272
  break;
236
273
  }
237
274
  case 'await': {
238
- scanExpression(expr.value, boardConstants, parentLine, parentCol, diagnostics);
275
+ scanExpression(expr.value, boardConstants, parentLine, parentCol, parentFilePath, diagnostics);
239
276
  break;
240
277
  }
241
278
  case 'hal-expr': {
242
279
  // Expression-form HAL operation (e.g. adc.read used as a value).
243
- checkCapability(expr.operation, parentLine, parentCol, boardConstants, diagnostics);
280
+ checkCapability(expr.operation, parentLine, parentCol, parentFilePath, boardConstants, diagnostics);
244
281
  break;
245
282
  }
246
283
  case 'number':
@@ -249,6 +286,7 @@ function scanExpression(expr, boardConstants, parentLine, parentCol, diagnostics
249
286
  case 'identifier':
250
287
  case 'raw':
251
288
  case 'tuple-access':
289
+ case 'call':
252
290
  break;
253
291
  default:
254
292
  assertNever(expr);
@@ -259,20 +297,21 @@ function scanStatement(stmt, boardConstants, diagnostics) {
259
297
  return;
260
298
  const line = stmt.sourceSpan?.startLine;
261
299
  const col = stmt.sourceSpan?.startColumn;
300
+ const filePath = stmt.sourceSpan?.filePath;
262
301
  switch (stmt.kind) {
263
302
  case 'var_decl': {
264
303
  if (stmt.initializer)
265
- scanExpression(stmt.initializer, boardConstants, line, col, diagnostics);
304
+ scanExpression(stmt.initializer, boardConstants, line, col, filePath, diagnostics);
266
305
  break;
267
306
  }
268
307
  case 'assign': {
269
308
  if (stmt.value)
270
- scanExpression(stmt.value, boardConstants, line, col, diagnostics);
309
+ scanExpression(stmt.value, boardConstants, line, col, filePath, diagnostics);
271
310
  break;
272
311
  }
273
312
  case 'if': {
274
313
  if (stmt.condition)
275
- scanExpression(stmt.condition, boardConstants, line, col, diagnostics);
314
+ scanExpression(stmt.condition, boardConstants, line, col, filePath, diagnostics);
276
315
  for (const s of stmt.thenBranch)
277
316
  scanStatement(s, boardConstants, diagnostics);
278
317
  if (stmt.elseBranch)
@@ -283,14 +322,14 @@ function scanStatement(stmt, boardConstants, diagnostics) {
283
322
  case 'while':
284
323
  case 'do_while': {
285
324
  if (stmt.condition)
286
- scanExpression(stmt.condition, boardConstants, line, col, diagnostics);
325
+ scanExpression(stmt.condition, boardConstants, line, col, filePath, diagnostics);
287
326
  for (const s of stmt.body)
288
327
  scanStatement(s, boardConstants, diagnostics);
289
328
  break;
290
329
  }
291
330
  case 'for': {
292
331
  if (stmt.condition)
293
- scanExpression(stmt.condition, boardConstants, line, col, diagnostics);
332
+ scanExpression(stmt.condition, boardConstants, line, col, filePath, diagnostics);
294
333
  if (stmt.initializer)
295
334
  scanStatement(stmt.initializer, boardConstants, diagnostics);
296
335
  if (stmt.increment)
@@ -309,23 +348,23 @@ function scanStatement(stmt, boardConstants, diagnostics) {
309
348
  }
310
349
  case 'return': {
311
350
  if (stmt.value)
312
- scanExpression(stmt.value, boardConstants, line, col, diagnostics);
351
+ scanExpression(stmt.value, boardConstants, line, col, filePath, diagnostics);
313
352
  break;
314
353
  }
315
354
  case 'call': {
316
355
  for (const arg of stmt.args) {
317
- scanExpression(arg, boardConstants, line, col, diagnostics);
356
+ scanExpression(arg, boardConstants, line, col, filePath, diagnostics);
318
357
  }
319
358
  break;
320
359
  }
321
360
  case 'hal-op': {
322
361
  // Statement-form HAL operation — the primary capability check target.
323
- checkCapability(stmt.operation, line, col, boardConstants, diagnostics);
362
+ checkCapability(stmt.operation, line, col, filePath, boardConstants, diagnostics);
324
363
  break;
325
364
  }
326
365
  case 'switch': {
327
366
  if (stmt.expression)
328
- scanExpression(stmt.expression, boardConstants, line, col, diagnostics);
367
+ scanExpression(stmt.expression, boardConstants, line, col, filePath, diagnostics);
329
368
  for (const c of stmt.cases) {
330
369
  for (const s of c.body)
331
370
  scanStatement(s, boardConstants, diagnostics);
@@ -355,7 +394,7 @@ function scanStatement(stmt, boardConstants, diagnostics) {
355
394
  }
356
395
  case 'throw': {
357
396
  if (stmt.value)
358
- scanExpression(stmt.value, boardConstants, line, col, diagnostics);
397
+ scanExpression(stmt.value, boardConstants, line, col, filePath, diagnostics);
359
398
  break;
360
399
  }
361
400
  case 'update':
@@ -4,7 +4,7 @@ import type { Diagnostic } from '../types.js';
4
4
  * Validate that GPIO I/O operations are preceded by mode configuration.
5
5
  *
6
6
  * - Read without prior mode → **warning** (undefined behavior on floating pin)
7
- * - Write without prior mode → **info** (Arduino implicitly sets OUTPUT, but
8
- * explicit configuration is recommended)
7
+ * - Write without prior mode → **info** (Wiring-derived frameworks implicitly
8
+ * set OUTPUT, but explicit configuration is recommended)
9
9
  */
10
10
  export declare function validatePinModeConfig(program: ProgramIR): Diagnostic[];
@@ -5,6 +5,7 @@
5
5
  // configuration. Generates warnings for reads and info for writes.
6
6
  // ---------------------------------------------------------------------------
7
7
  import { scanNestedStatements } from './interrupt-analysis.js';
8
+ import { hasLoadedFramework, getLoadedFramework } from '../framework-registry.js';
8
9
  /** Methods that configure pin mode. */
9
10
  const MODE_SET_METHODS = new Set([
10
11
  'inputPullUp', 'inputPullDown', 'outputOpenDrain',
@@ -14,7 +15,7 @@ const MODE_SET_METHODS = new Set([
14
15
  const READ_METHODS = new Set([
15
16
  'read', 'isHigh', 'isLow', 'readAnalog', 'readVoltage',
16
17
  ]);
17
- /** Write operations that implicitly set OUTPUT mode on Arduino. */
18
+ /** Write operations that implicitly set OUTPUT mode on Wiring-derived frameworks. */
18
19
  const WRITE_METHODS = new Set([
19
20
  'write', 'high', 'low', 'toggle', 'pulse', 'pwm', 'tone',
20
21
  ]);
@@ -26,8 +27,8 @@ const PIN_RECEIVER_KINDS = new Set([
26
27
  * Validate that GPIO I/O operations are preceded by mode configuration.
27
28
  *
28
29
  * - Read without prior mode → **warning** (undefined behavior on floating pin)
29
- * - Write without prior mode → **info** (Arduino implicitly sets OUTPUT, but
30
- * explicit configuration is recommended)
30
+ * - Write without prior mode → **info** (Wiring-derived frameworks implicitly
31
+ * set OUTPUT, but explicit configuration is recommended)
31
32
  */
32
33
  export function validatePinModeConfig(program) {
33
34
  const diagnostics = [];
@@ -43,6 +44,7 @@ export function validatePinModeConfig(program) {
43
44
  message: `Analog pin '${receiver}' used as digital output. ` +
44
45
  `Analog input capability (ADC) is lost while pin is in OUTPUT mode. ` +
45
46
  `Call ${receiver}.asInput() to restore analog reading.`,
47
+ filePath: program.fileName,
46
48
  code: 'analog-pin-as-output',
47
49
  source: 'pin-mode-validation',
48
50
  });
@@ -55,6 +57,7 @@ export function validatePinModeConfig(program) {
55
57
  severity: 'warning',
56
58
  message: `Pin '${receiver}' read via '${method}()' without prior mode configuration. ` +
57
59
  `Call ${receiver}.asInput() or ${receiver}.inputPullUp() first.`,
60
+ filePath: program.fileName,
58
61
  code: 'pin-mode-not-set',
59
62
  source: 'pin-mode-validation',
60
63
  });
@@ -63,7 +66,8 @@ export function validatePinModeConfig(program) {
63
66
  diagnostics.push({
64
67
  severity: 'info',
65
68
  message: `Pin '${receiver}' written via '${method}()' without explicit mode configuration. ` +
66
- `Arduino implicitly sets OUTPUT, but explicit ${receiver}.asOutput() is recommended.`,
69
+ `The target framework implicitly sets OUTPUT, but explicit ${receiver}.asOutput() is recommended.`,
70
+ filePath: program.fileName,
67
71
  code: 'pin-mode-not-set',
68
72
  source: 'pin-mode-validation',
69
73
  });
@@ -100,6 +104,7 @@ export function validatePinModeConfig(program) {
100
104
  severity: 'warning',
101
105
  message: `Pin ${op.pin} read without prior mode configuration. ` +
102
106
  `Call asInput() or inputPullUp() first — reading a floating pin is undefined behavior.`,
107
+ filePath: program.fileName,
103
108
  code: 'pin-mode-not-set',
104
109
  source: 'pin-mode-validation',
105
110
  });
@@ -108,7 +113,8 @@ export function validatePinModeConfig(program) {
108
113
  diagnostics.push({
109
114
  severity: 'info',
110
115
  message: `Pin ${op.pin} written without explicit mode configuration. ` +
111
- `Arduino implicitly sets OUTPUT, but explicit asOutput() is recommended.`,
116
+ `The target framework implicitly sets OUTPUT, but explicit asOutput() is recommended.`,
117
+ filePath: program.fileName,
112
118
  code: 'pin-mode-not-set',
113
119
  source: 'pin-mode-validation',
114
120
  });
@@ -143,21 +149,51 @@ export function validatePinModeConfig(program) {
143
149
  // Raw nodes: after full HAL resolution, some pin ops land as their C++ text
144
150
  // (e.g. `digitalRead(2)`, `digitalWrite(2, 1)`) rather than structured
145
151
  // hal-expr nodes. Extract the pin number and apply the same mode check.
146
- // This mirrors how peripheral-usage.ts detects gpio via emitted text.
152
+ // Detect GPIO ops in lowered raw C++ text. Build the read/write name
153
+ // patterns from the loaded framework's HAL vocabulary (halCallNames),
154
+ // classifying names containing "Read" as reads and "Write" as writes (the
155
+ // Wiring-derived convention). Seed with digitalRead/digitalWrite as the
156
+ // canonical Wiring forms so the validator works even when no framework is
157
+ // loaded. Non-Wiring frameworks that emit other call forms register them
158
+ // via halCallNames; this loop picks them up.
147
159
  if (e.kind === 'raw' && typeof e.value === 'string') {
148
- const readMatch = e.value.match(/digitalRead\((\d+)\)/);
149
- const writeMatch = e.value.match(/digitalWrite\((\d+)/);
150
- const toggleMatch = e.value.match(/digitalWrite\((\d+),\s*digitalRead/);
151
- const pin = readMatch ? parseInt(readMatch[1], 10)
152
- : writeMatch ? parseInt(writeMatch[1], 10)
153
- : null;
154
- if (pin !== null) {
155
- const op = toggleMatch
156
- ? { operation: 'gpio.toggle', pin }
157
- : readMatch ? { operation: 'gpio.read', pin }
158
- : { operation: 'gpio.write', pin };
159
- checkGpioHalOp(op);
160
+ const frameworkHalNames = hasLoadedFramework()
161
+ ? getLoadedFramework().strategy.halCallNames?.()
162
+ : undefined;
163
+ const halNames = frameworkHalNames ?? new Set(['digitalRead', 'digitalWrite']);
164
+ const readNames = [...halNames].filter(n => /read/i.test(n));
165
+ const writeNames = [...halNames].filter(n => /write/i.test(n));
166
+ let pin = null;
167
+ let op = null;
168
+ for (const rn of readNames) {
169
+ const esc = rn.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
170
+ const m = e.value.match(new RegExp(`${esc}\\((\\d+)\\)`));
171
+ if (m) {
172
+ pin = parseInt(m[1], 10);
173
+ op = { operation: 'gpio.read', pin };
174
+ break;
175
+ }
176
+ }
177
+ if (!op) {
178
+ for (const wn of writeNames) {
179
+ const esc = wn.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
180
+ const m = e.value.match(new RegExp(`${esc}\\((\\d+)`));
181
+ if (m) {
182
+ pin = parseInt(m[1], 10);
183
+ op = { operation: 'gpio.write', pin };
184
+ break;
185
+ }
186
+ }
187
+ }
188
+ // toggle: a write call whose second arg is a read call on the same pin.
189
+ if (!op) {
190
+ const tm = e.value.match(/\((\d+),\s*\w*[Rr]ead/);
191
+ if (tm) {
192
+ op = { operation: 'gpio.toggle', pin: parseInt(tm[1], 10) };
193
+ }
160
194
  }
195
+ if (op)
196
+ checkGpioHalOp(op);
161
197
  }
162
198
  // Recurse into nested expressions
163
199
  if (e.args && Array.isArray(e.args)) {
@@ -8,4 +8,4 @@ import type { Diagnostic } from '../types.js';
8
8
  * @param boardConstants - Board constants containing pins.unsafe array
9
9
  * @returns Array of diagnostic warnings for unsafe pin usage
10
10
  */
11
- export declare function validateUnsafePins(usage: PeripheralUsage, boardConstants: BoardConstants | undefined): Diagnostic[];
11
+ export declare function validateUnsafePins(usage: PeripheralUsage, boardConstants: BoardConstants | undefined, filePath: string): Diagnostic[];
@@ -30,7 +30,7 @@ function buildUnsafePinMessage(pinName, boardConstants) {
30
30
  * @param boardConstants - Board constants containing pins.unsafe array
31
31
  * @returns Array of diagnostic warnings for unsafe pin usage
32
32
  */
33
- export function validateUnsafePins(usage, boardConstants) {
33
+ export function validateUnsafePins(usage, boardConstants, filePath) {
34
34
  const diagnostics = [];
35
35
  if (!boardConstants)
36
36
  return diagnostics;
@@ -50,6 +50,7 @@ export function validateUnsafePins(usage, boardConstants) {
50
50
  severity: 'warning',
51
51
  message: buildUnsafePinMessage(pinName, boardConstants),
52
52
  code: 'unsafe-pin-usage',
53
+ filePath,
53
54
  source: 'pin-safety',
54
55
  });
55
56
  }
@@ -29,6 +29,22 @@ export interface ProgramAnalysisResult {
29
29
  usesTiming: boolean;
30
30
  usesWDT: boolean;
31
31
  usesStrPtr: boolean;
32
+ usesUart: boolean;
33
+ usesSPI: boolean;
34
+ usesI2C: boolean;
35
+ usesEEPROM: boolean;
36
+ usesTone: boolean;
37
+ /** map()/constrain() Arduino-API calls. framework-avr gates its native
38
+ * _native_map/_native_constrain helpers on these (they're dead code
39
+ * otherwise — no other internal caller references them). */
40
+ usesMap: boolean;
41
+ usesConstrain: boolean;
42
+ /** Comprehensive timing gate for the native millis() Timer0 ISR on AVR.
43
+ * True when the program directly uses millis/delay/micros, OR has hidden
44
+ * consumers of the soft clock: setInterval/setTimeout (timerCallCount),
45
+ * async functions (the runtime polls millis), or a mounted UI (per-frame
46
+ * tick injected by the emitter, not present in user source). */
47
+ usesNativeTiming: boolean;
32
48
  hasSerialBegin: boolean;
33
49
  hasGenerators: boolean;
34
50
  usesStdMap: boolean;
@@ -36,6 +52,71 @@ export interface ProgramAnalysisResult {
36
52
  * __tc_TimerRuntime::MAX_TIMERS to the observed count (floor 1) rather than
37
53
  * a blind constant, so a one-timer program links one slot, not eight. */
38
54
  timerCallCount: number;
55
+ /** ESP32 peripheral usage — framework-esp32 gates its IDF driver blocks and
56
+ * forced includes on these. Detected from HAL-op operation names, the same
57
+ * way usesUart/usesSPI/usesI2C are. Other frameworks have no CUTTLEFISH_*
58
+ * blocks with these marker names so the setup.ts filters are no-ops there. */
59
+ usesGPIO: boolean;
60
+ usesPWM: boolean;
61
+ usesRmt: boolean;
62
+ usesADC: boolean;
63
+ usesDAC: boolean;
64
+ usesPower: boolean;
65
+ usesWdt: boolean;
66
+ usesInterrupts: boolean;
67
+ usesPulse: boolean;
68
+ usesShift: boolean;
69
+ /** WiFi / HTTP / BLE usage — framework-esp32 gates the __tc_wifi/__tc_http/
70
+ * __tc_ble runtime shims and their esp_wifi/esp_http_client/nimble includes
71
+ * on these. Detected from wifi.* / http.* / ble.* HAL-op operation names. */
72
+ usesWifi: boolean;
73
+ /** Fine-grained WiFi sub-feature tracking — gates individual shim sub-blocks
74
+ * so that unused static helper functions don't trigger -Wunused-function. */
75
+ usesWifiConnect: boolean;
76
+ usesWifiConnectBlocking: boolean;
77
+ usesWifiQuery: boolean;
78
+ usesWifiScan: boolean;
79
+ usesWifiConfig: boolean;
80
+ usesHttp: boolean;
81
+ usesBle: boolean;
82
+ /** Preferences (NVS) usage — framework-esp32 gates the __tc_prefs runtime shim
83
+ * and its nvs_flash/nvs includes on this. Detected from preferences.* ops. */
84
+ usesPreferences: boolean;
85
+ /** Random usage — frameworks gate their PRNG runtime/include on this
86
+ * (e.g. framework-esp32 includes <esp_random.h>). Detected from random.* ops. */
87
+ usesRandom: boolean;
88
+ /** FS (filesystem) usage — frameworks gate their VFS/SD runtime shim on this.
89
+ * Detected from fs.* ops. */
90
+ usesFS: boolean;
91
+ /** mDNS usage — frameworks gate the esp_mdns runtime/include on this. */
92
+ usesMdns: boolean;
93
+ /** MQTT usage — frameworks gate the esp_mqtt runtime/include on this. */
94
+ usesMqtt: boolean;
95
+ /** OTA usage — frameworks gate the esp_https_ota runtime/include on this. */
96
+ usesOta: boolean;
97
+ /** Temperature (die temp) usage. Detected from temp.* ops. */
98
+ usesTemp: boolean;
99
+ /** Hardware timer (GPTimer) usage. Detected from hwtimer.* ops. */
100
+ usesHwtimer: boolean;
101
+ /** Capacitive touch pins usage. Detected from capacitive.* ops. */
102
+ usesCapacitive: boolean;
103
+ /** Worker offload usage. Detected from worker.* ops. Frameworks gate the
104
+ * worker_runtime polyfill (and its per-framework backing) on this. */
105
+ usesWorker: boolean;
106
+ /** Native/desktop: std::chrono / steady_clock usage (gates <chrono>). */
107
+ usesChrono: boolean;
108
+ /** Native/desktop: std::set usage (gates <set>). */
109
+ usesSet: boolean;
110
+ /** Native/desktop: std::algorithm usage (std::sort/find/transform etc., gates <algorithm>). */
111
+ usesAlgorithm: boolean;
112
+ /** Native/desktop: cstdio usage (printf/fprintf/snprintf, gates <cstdio>). */
113
+ usesCstdio: boolean;
114
+ /** digitalRead() in lowered raw text or gpio.read hal-op (gates the native digitalRead shim). */
115
+ usesDigitalRead: boolean;
116
+ /** display.* hal-op present, or a UI is mounted (replaces ad-hoc programUsesDisplay walks). */
117
+ usesDisplay: boolean;
118
+ /** cuttlefish_halt() in lowered raw text (gates the cuttlefish_halt macro polyfill). */
119
+ usesHalt: boolean;
39
120
  }
40
121
  /**
41
122
  * Analyze a program IR in a single pass to detect all features.