@typecad/cuttlefish 0.1.0-alpha.2 → 1.0.0-alpha.11
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.
- package/README.md +10 -10
- package/dist/api/board-types.d.ts +1 -1
- package/dist/api/config.d.ts +38 -2
- package/dist/api/index.d.ts +1 -1
- package/dist/api/schema/types.d.ts +15 -0
- package/dist/api/shared/async-runtime-static.d.ts +12 -1
- package/dist/api/shared/async-runtime-static.js +69 -24
- package/dist/api/shared/async-symbol-detector.d.ts +11 -0
- package/dist/api/shared/async-symbol-detector.js +140 -0
- package/dist/api/shared/async-types.d.ts +24 -0
- package/dist/api/shared/coop-scheduler.d.ts +60 -0
- package/dist/api/shared/coop-scheduler.js +149 -0
- package/dist/api/shared/display-adapter.d.ts +4 -3
- package/dist/api/shared/display-adapter.js +15 -118
- package/dist/api/shared/display-adapters/sdl.js +38 -31
- package/dist/api/shared/display-profile.d.ts +47 -25
- package/dist/api/shared/display-profile.js +31 -164
- package/dist/api/shared/framework-manifest-registry.d.ts +9 -0
- package/dist/api/shared/framework-manifest-registry.js +27 -0
- package/dist/api/shared/framework-manifest.d.ts +595 -0
- package/dist/api/shared/framework-manifest.js +193 -0
- package/dist/api/shared/glcdfont.d.ts +12 -0
- package/dist/api/shared/glcdfont.js +124 -0
- package/dist/api/shared/graphics-strategy.d.ts +37 -0
- package/dist/api/shared/hal-op-ir.d.ts +764 -1
- package/dist/api/shared/hal-op-ir.js +129 -1
- package/dist/api/shared/index.d.ts +22 -2
- package/dist/api/shared/index.js +26 -1
- package/dist/api/shared/ir-core.d.ts +4 -0
- package/dist/api/shared/ir-declarations.d.ts +6 -0
- package/dist/api/shared/native-display-op-resolver.d.ts +10 -0
- package/dist/api/shared/native-display-op-resolver.js +64 -0
- package/dist/api/shared/platform-strategy.d.ts +119 -2
- package/dist/api/shared/polyfill-helper-registry.js +18 -1
- package/dist/api/shared/promise-runtime.d.ts +1 -1
- package/dist/api/shared/promise-runtime.js +95 -13
- package/dist/api/shared/spdx-licenses.d.ts +139 -0
- package/dist/api/shared/spdx-licenses.js +385 -0
- package/dist/api/shared/toolchain-types.d.ts +17 -0
- package/dist/api/shared/types.d.ts +8 -0
- package/dist/api/shared/validate-framework-manifest.d.ts +28 -0
- package/dist/api/shared/validate-framework-manifest.js +494 -0
- package/dist/api/shared/worker-runtime-polyfill.d.ts +22 -0
- package/dist/api/shared/worker-runtime-polyfill.js +34 -0
- package/dist/api/shared/worker-runtime.d.ts +69 -0
- package/dist/api/shared/worker-runtime.js +163 -0
- package/dist/cli-utils.d.ts +1 -0
- package/dist/cli-utils.js +3 -1
- package/dist/cli.js +249 -32
- package/dist/config-loader.d.ts +7 -2
- package/dist/config-loader.js +23 -3
- package/dist/config-schema.d.ts +112 -70
- package/dist/config-schema.js +14 -0
- package/dist/contract/board-generator.d.ts +17 -0
- package/dist/contract/board-generator.js +57 -0
- package/dist/contract/contract-parser.d.ts +217 -0
- package/dist/contract/contract-parser.js +224 -0
- package/dist/contract/index.d.ts +22 -0
- package/dist/contract/index.js +68 -0
- package/dist/create/board-codegen.js +4 -4
- package/dist/create/board-generators.js +4 -5
- package/dist/create/board-spec.d.ts +72 -75
- package/dist/create/board-spec.js +0 -1
- package/dist/create/framework-catalog.d.ts +64 -0
- package/dist/create/framework-catalog.js +136 -0
- package/dist/create/index.d.ts +5 -1
- package/dist/create/index.js +3 -1
- package/dist/create/init-scaffold.d.ts +12 -3
- package/dist/create/init-scaffold.js +49 -14
- package/dist/create/init-templates.d.ts +4 -0
- package/dist/create/init-templates.js +271 -17
- package/dist/create/init-wizard.js +41 -27
- package/dist/create/install-deps.d.ts +32 -0
- package/dist/create/install-deps.js +46 -0
- package/dist/debug/preprocessor.js +178 -39
- package/dist/debug/types.d.ts +23 -0
- package/dist/diagnostics/mermaid-builder.d.ts +1 -1
- package/dist/diagnostics/mermaid-builder.js +34 -24
- package/dist/emit/compliance/arxml-writer.d.ts +11 -0
- package/dist/emit/compliance/arxml-writer.js +34 -0
- package/dist/emit/compliance/compliance-context.d.ts +57 -0
- package/dist/emit/compliance/compliance-context.js +113 -0
- package/dist/emit/compliance/deviation-ledger.d.ts +27 -0
- package/dist/emit/compliance/deviation-ledger.js +47 -0
- package/dist/emit/compliance/deviation-writer.d.ts +30 -0
- package/dist/emit/compliance/deviation-writer.js +37 -0
- package/dist/emit/compliance/index.d.ts +7 -0
- package/dist/emit/compliance/index.js +6 -0
- package/dist/emit/compliance/rule-engine.d.ts +13 -0
- package/dist/emit/compliance/rule-engine.js +101 -0
- package/dist/emit/compliance/rules.d.ts +16 -0
- package/dist/emit/compliance/rules.js +191 -0
- package/dist/emit/compliance/types.d.ts +59 -0
- package/dist/emit/compliance/types.js +8 -0
- package/dist/emit/cpp-emitter.js +4 -3
- package/dist/emit/emitters/class-emitter.js +6 -1
- package/dist/emit/emitters/emitter-context.d.ts +28 -1
- package/dist/emit/emitters/function-emitter-impl.js +153 -53
- package/dist/emit/emitters/line-appender.js +19 -0
- package/dist/emit/emitters/line-marker.d.ts +38 -0
- package/dist/emit/emitters/line-marker.js +39 -0
- package/dist/emit/emitters/output-finalizer.d.ts +6 -0
- package/dist/emit/emitters/output-finalizer.js +101 -12
- package/dist/emit/emitters/setup.d.ts +17 -0
- package/dist/emit/emitters/setup.js +372 -30
- package/dist/emit/emitters/top-level-prep.js +8 -0
- package/dist/emit/emitters/type-decl-emitter.js +28 -3
- package/dist/emit/emitters/ui-emitter.js +33 -9
- package/dist/emit/expression-renderer.d.ts +1 -1
- package/dist/emit/expression-renderer.js +91 -6
- package/dist/emit/route-hal-op.js +18 -5
- package/dist/emit/snprintf-helpers.js +15 -4
- package/dist/emit/statement-renderer.d.ts +10 -0
- package/dist/emit/statement-renderer.js +34 -5
- package/dist/emit/utils/async-state-machine.js +221 -125
- package/dist/emit/utils/hal-op-cpp-type.d.ts +6 -0
- package/dist/emit/utils/hal-op-cpp-type.js +40 -0
- package/dist/framework-package.js +2 -0
- package/dist/framework-registry.d.ts +17 -0
- package/dist/install/framework-catalog.d.ts +53 -0
- package/dist/install/framework-catalog.js +107 -0
- package/dist/install/handle-install.d.ts +35 -0
- package/dist/install/handle-install.js +177 -0
- package/dist/install/index.d.ts +4 -0
- package/dist/install/index.js +3 -0
- package/dist/ir/adc-range-validation.js +40 -26
- package/dist/ir/build-ir-state.d.ts +1 -0
- package/dist/ir/build-ir-state.js +14 -0
- package/dist/ir/build-ir.js +9 -5
- package/dist/ir/call-graph.js +16 -0
- package/dist/ir/expression-to-ir.js +207 -6
- package/dist/ir/feature-registry.js +7 -25
- package/dist/ir/function-builder.js +22 -0
- package/dist/ir/hal/hal-emitter.d.ts +5 -2
- package/dist/ir/hal/hal-emitter.js +50 -18
- package/dist/ir/hal/hal-parser.d.ts +6 -0
- package/dist/ir/hal/hal-parser.js +81 -0
- package/dist/ir/hal/hal-plugins.js +733 -1
- package/dist/ir/identifier-collector.js +35 -0
- package/dist/ir/interrupt-analysis.d.ts +5 -1
- package/dist/ir/interrupt-analysis.js +16 -19
- package/dist/ir/memory-budget-validation.js +1 -0
- package/dist/ir/network-validation.d.ts +4 -0
- package/dist/ir/network-validation.js +184 -0
- package/dist/ir/ownership-analysis.js +39 -1
- package/dist/ir/peripheral-ownership.js +5 -0
- package/dist/ir/peripheral-validation.d.ts +1 -1
- package/dist/ir/peripheral-validation.js +6 -3
- package/dist/ir/pin-alias-conflict.d.ts +1 -1
- package/dist/ir/pin-alias-conflict.js +2 -1
- package/dist/ir/pin-capability-validation.js +71 -32
- package/dist/ir/pin-mode-validation.d.ts +2 -2
- package/dist/ir/pin-mode-validation.js +54 -18
- package/dist/ir/pin-safety.d.ts +1 -1
- package/dist/ir/pin-safety.js +2 -1
- package/dist/ir/program-analysis.d.ts +81 -0
- package/dist/ir/program-analysis.js +429 -0
- package/dist/ir/pulldown-validation.d.ts +1 -1
- package/dist/ir/pulldown-validation.js +2 -1
- package/dist/ir/pwm-timer-sharing.d.ts +1 -1
- package/dist/ir/pwm-timer-sharing.js +2 -1
- package/dist/ir/render-expr.js +11 -0
- package/dist/ir/resource-analysis.js +2 -0
- package/dist/ir/timer0-pwm-timing-conflict.d.ts +1 -1
- package/dist/ir/timer0-pwm-timing-conflict.js +2 -1
- package/dist/ir/timing-validation.d.ts +6 -1
- package/dist/ir/timing-validation.js +61 -13
- package/dist/ir/transformers/call-statement.js +105 -0
- package/dist/ir/transformers/expressions.js +62 -0
- package/dist/ir/transformers/hal-call-resolver.js +21 -0
- package/dist/ir/transformers/hal-emit-helpers.js +1 -1
- package/dist/ir/transformers/namespace-methods.js +17 -12
- package/dist/ir/transformers/ui-reactive.js +2 -2
- package/dist/ir/transformers/variables.js +137 -19
- package/dist/ir/try-catch-validation.js +2 -0
- package/dist/ir/type-resolution.js +20 -2
- package/dist/ir/ui-element-auto-wire.js +7 -5
- package/dist/ir/unit-suspicion-validation.js +9 -7
- package/dist/ir/utils/map-statements.d.ts +4 -0
- package/dist/ir/utils/map-statements.js +79 -0
- package/dist/ir/validation-orchestrator.js +15 -8
- package/dist/ir/worker-analysis.d.ts +10 -0
- package/dist/ir/worker-analysis.js +261 -0
- package/dist/libdef/c-to-decl.d.ts +27 -0
- package/dist/libdef/c-to-decl.js +397 -0
- package/dist/libdef/component-decls.d.ts +2 -0
- package/dist/libdef/component-decls.js +6 -0
- package/dist/libdef/component-discovery.d.ts +43 -0
- package/dist/libdef/component-discovery.js +83 -0
- package/dist/libdef/cpp-to-decl.d.ts +9 -0
- package/dist/libdef/cpp-to-decl.js +72 -0
- package/dist/libdef/registry.js +5 -2
- package/dist/lint-cache.d.ts +59 -0
- package/dist/lint-cache.js +257 -0
- package/dist/orchestrator/graph-builder.js +14 -9
- package/dist/platform/coop-scheduler-runtime.d.ts +19 -0
- package/dist/platform/coop-scheduler-runtime.js +41 -0
- package/dist/platform/generic-strategy.d.ts +15 -3
- package/dist/platform/generic-strategy.js +49 -4
- package/dist/safety/safety-bridge.d.ts +11 -0
- package/dist/safety/safety-bridge.js +48 -0
- package/dist/safety/sidecar-bridge.d.ts +5 -0
- package/dist/safety/sidecar-bridge.js +22 -0
- package/dist/safety-hook.d.ts +79 -0
- package/dist/safety-hook.js +35 -0
- package/dist/testing.d.ts +8 -2
- package/dist/testing.js +9 -2
- package/dist/transpile.d.ts +3 -0
- package/dist/transpile.js +191 -49
- package/dist/types.d.ts +22 -3
- package/dist/ui-hook.d.ts +17 -3
- package/dist/utils/cli.js +123 -2
- package/dist/utils/fs.d.ts +13 -0
- package/dist/utils/fs.js +50 -0
- package/dist/utils/ui.js +3 -1
- package/package.json +23 -3
- package/dist/api/shared/display-adapters/eink-mono.d.ts +0 -2
- package/dist/api/shared/display-adapters/eink-mono.js +0 -53
- package/dist/api/shared/display-adapters/ssd1309.d.ts +0 -2
- package/dist/api/shared/display-adapters/ssd1309.js +0 -136
- package/dist/api/shared/display-adapters/st7796.d.ts +0 -2
- package/dist/api/shared/display-adapters/st7796.js +0 -132
- package/dist/ir/heap-array-validation.d.ts +0 -24
- package/dist/ir/heap-array-validation.js +0 -29
|
@@ -38,6 +38,20 @@ function analyzeExpression(expr, result, strategy) {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
+
// Timer polyfill sizing — Timing.setInterval/Timing.setTimeout in
|
|
42
|
+
// expression position (e.g. `const id = Timing.setInterval(...)`) lower to
|
|
43
|
+
// a raw expression whose value contains `__tc_setInterval(...)`. The
|
|
44
|
+
// callee-based counter in the "call" case only fires for bare timer
|
|
45
|
+
// calls; without this the timer_methods polyfill is gated off and the
|
|
46
|
+
// link fails: "'__tc_setInterval' was not declared in this scope".
|
|
47
|
+
{
|
|
48
|
+
const intervals = expr.value.match(/__tc_setInterval\s*\(/g);
|
|
49
|
+
if (intervals)
|
|
50
|
+
result.timerCallCount += intervals.length;
|
|
51
|
+
const timeouts = expr.value.match(/__tc_setTimeout\s*\(/g);
|
|
52
|
+
if (timeouts)
|
|
53
|
+
result.timerCallCount += timeouts.length;
|
|
54
|
+
}
|
|
41
55
|
if (/\bString\s*\(/.test(expr.value)) {
|
|
42
56
|
result.usesStringConversion = true;
|
|
43
57
|
}
|
|
@@ -63,6 +77,24 @@ function analyzeExpression(expr, result, strategy) {
|
|
|
63
77
|
if (expr.value.includes('__tc_str_ptr')) {
|
|
64
78
|
result.usesStrPtr = true;
|
|
65
79
|
}
|
|
80
|
+
if (/std::chrono|steady_clock/.test(expr.value)) {
|
|
81
|
+
result.usesChrono = true;
|
|
82
|
+
}
|
|
83
|
+
if (/std::set\s*</.test(expr.value)) {
|
|
84
|
+
result.usesSet = true;
|
|
85
|
+
}
|
|
86
|
+
if (/std::(sort|find|transform|copy|fill|remove|count|reverse|accumulate)\s*\(/.test(expr.value)) {
|
|
87
|
+
result.usesAlgorithm = true;
|
|
88
|
+
}
|
|
89
|
+
if (/\b(printf|fprintf|sprintf|snprintf)\s*\(/.test(expr.value)) {
|
|
90
|
+
result.usesCstdio = true;
|
|
91
|
+
}
|
|
92
|
+
if (/\bdigitalRead\s*\(/.test(expr.value)) {
|
|
93
|
+
result.usesDigitalRead = true;
|
|
94
|
+
}
|
|
95
|
+
if (/cuttlefish_halt\s*\(/.test(expr.value)) {
|
|
96
|
+
result.usesHalt = true;
|
|
97
|
+
}
|
|
66
98
|
break;
|
|
67
99
|
case "method-call":
|
|
68
100
|
if (strategy.isConsoleCall(expr.callee)) {
|
|
@@ -102,6 +134,30 @@ function analyzeExpression(expr, result, strategy) {
|
|
|
102
134
|
if (expr.callee.startsWith("WDT.") || expr.callee === "WDT") {
|
|
103
135
|
result.usesWDT = true;
|
|
104
136
|
}
|
|
137
|
+
// Native AVR peripheral usage from namespace-prefixed method calls
|
|
138
|
+
// (Serial.* / SPI.* / Wire.* / EEPROM.*). The HAL resolver lowers these
|
|
139
|
+
// to structured hal-ops (detected in analyzeStatement) OR to bare
|
|
140
|
+
// lowered calls; these checks cover the pre-lowering and direct forms.
|
|
141
|
+
if (expr.callee.startsWith("Serial.") || expr.callee === "Serial") {
|
|
142
|
+
result.usesUart = true;
|
|
143
|
+
}
|
|
144
|
+
if (expr.callee.startsWith("SPI.") || expr.callee === "SPI") {
|
|
145
|
+
result.usesSPI = true;
|
|
146
|
+
}
|
|
147
|
+
if (expr.callee.startsWith("Wire.") || expr.callee === "Wire") {
|
|
148
|
+
result.usesI2C = true;
|
|
149
|
+
}
|
|
150
|
+
if (expr.callee.startsWith("EEPROM.") || expr.callee === "EEPROM") {
|
|
151
|
+
result.usesEEPROM = true;
|
|
152
|
+
}
|
|
153
|
+
// map()/constrain() Arduino-API calls appear as method-call exprs
|
|
154
|
+
// (callee "map"/"constrain"). framework-avr lowers these to _native_map/
|
|
155
|
+
// _native_constrain via symbol aliases, so the helpers are dead code
|
|
156
|
+
// unless the program actually calls them.
|
|
157
|
+
if (expr.callee === "map")
|
|
158
|
+
result.usesMap = true;
|
|
159
|
+
if (expr.callee === "constrain")
|
|
160
|
+
result.usesConstrain = true;
|
|
105
161
|
// Count setInterval/setTimeout call sites (post-rename callee names) so
|
|
106
162
|
// __tc_TimerRuntime::MAX_TIMERS can be sized to the observed count.
|
|
107
163
|
if (expr.callee === "__tc_setInterval" || expr.callee === "__tc_setTimeout"
|
|
@@ -192,6 +248,108 @@ function analyzeExpression(expr, result, strategy) {
|
|
|
192
248
|
analyzeExpression(expr.expression, result, strategy);
|
|
193
249
|
}
|
|
194
250
|
break;
|
|
251
|
+
case "hal-expr":
|
|
252
|
+
// Value-position HAL ops (e.g. `const v = D32.readAnalog()` lowers to an
|
|
253
|
+
// adc.read hal-expr initializer; `const ip = WiFi.localIP()` to a wifi
|
|
254
|
+
// hal-expr) never pass through the statement-level hal-op detection
|
|
255
|
+
// below, so mirror the FULL peripheral-flag detection here. Without this,
|
|
256
|
+
// a value-returning HAL op used in a var-init or expression context fails
|
|
257
|
+
// to set its analysis flag — the framework then drops the matching init
|
|
258
|
+
// block + forced includes (e.g. __tc_adc_read was emitted but the
|
|
259
|
+
// CUTTLEFISH_ADC init block and adc_oneshot.h include were not). Keep this
|
|
260
|
+
// list in sync with the hal-op statement case at the bottom of this file.
|
|
261
|
+
if (expr.operation && typeof expr.operation.operation === "string") {
|
|
262
|
+
const opName = expr.operation.operation;
|
|
263
|
+
if (opName.startsWith("display.")) {
|
|
264
|
+
result.usesGPIO = true;
|
|
265
|
+
result.usesDisplay = true;
|
|
266
|
+
}
|
|
267
|
+
if (opName.startsWith("gpio."))
|
|
268
|
+
result.usesGPIO = true;
|
|
269
|
+
if (opName === "gpio.read")
|
|
270
|
+
result.usesDigitalRead = true;
|
|
271
|
+
if (opName.startsWith("pwm."))
|
|
272
|
+
result.usesPWM = true;
|
|
273
|
+
if (opName.startsWith("rmt."))
|
|
274
|
+
result.usesRmt = true;
|
|
275
|
+
if (opName.startsWith("adc."))
|
|
276
|
+
result.usesADC = true;
|
|
277
|
+
if (opName.startsWith("dac."))
|
|
278
|
+
result.usesDAC = true;
|
|
279
|
+
if (opName.startsWith("power."))
|
|
280
|
+
result.usesPower = true;
|
|
281
|
+
if (opName.startsWith("wdt."))
|
|
282
|
+
result.usesWdt = true;
|
|
283
|
+
// Safety HAL ops (@typecad/safety) — map each op to the
|
|
284
|
+
// __tc_safety_* polyfill helper it triggers, so the mode-table and
|
|
285
|
+
// voter polyfills survive tree-shaking when actually used. Matches
|
|
286
|
+
// the resolveSafetyOp output naming. Cast through string because the
|
|
287
|
+
// HALOpIR operation union is closed and doesn't include safety.* ops
|
|
288
|
+
// (those are owned by the optional @typecad/safety package).
|
|
289
|
+
const safetyOpName = opName;
|
|
290
|
+
if (safetyOpName === "safety.record_pin_mode") {
|
|
291
|
+
result.usedPolyfillHelpers.add("__tc_safety_record_pin_mode");
|
|
292
|
+
}
|
|
293
|
+
if (safetyOpName === "safety.read_safe") {
|
|
294
|
+
result.usedPolyfillHelpers.add("__tc_safety_read_safe");
|
|
295
|
+
}
|
|
296
|
+
if (safetyOpName === "safety.write_verify") {
|
|
297
|
+
result.usedPolyfillHelpers.add("__tc_safety_write_verify");
|
|
298
|
+
}
|
|
299
|
+
if (opName.startsWith("interrupt."))
|
|
300
|
+
result.usesInterrupts = true;
|
|
301
|
+
if (opName.startsWith("pulse."))
|
|
302
|
+
result.usesPulse = true;
|
|
303
|
+
if (opName.startsWith("shift."))
|
|
304
|
+
result.usesShift = true;
|
|
305
|
+
if (opName.startsWith("tone."))
|
|
306
|
+
result.usesTone = true;
|
|
307
|
+
if (opName.startsWith("i2c."))
|
|
308
|
+
result.usesI2C = true;
|
|
309
|
+
if (opName.startsWith("spi."))
|
|
310
|
+
result.usesSPI = true;
|
|
311
|
+
if (opName.startsWith("uart."))
|
|
312
|
+
result.usesUart = true;
|
|
313
|
+
if (opName.startsWith("wifi.")) {
|
|
314
|
+
result.usesWifi = true;
|
|
315
|
+
if (opName === "wifi.connect" || opName === "wifi.connect_start" || opName === "wifi.disconnect") {
|
|
316
|
+
result.usesWifiConnect = true;
|
|
317
|
+
if (opName === "wifi.connect")
|
|
318
|
+
result.usesWifiConnectBlocking = true;
|
|
319
|
+
}
|
|
320
|
+
else if (opName.startsWith("wifi.scan"))
|
|
321
|
+
result.usesWifiScan = true;
|
|
322
|
+
else if (opName === "wifi.status" || opName === "wifi.local_ip" || opName === "wifi.rssi" || opName === "wifi.mac")
|
|
323
|
+
result.usesWifiQuery = true;
|
|
324
|
+
else if (opName === "wifi.set_hostname")
|
|
325
|
+
result.usesWifiConfig = true;
|
|
326
|
+
}
|
|
327
|
+
if (opName.startsWith("http."))
|
|
328
|
+
result.usesHttp = true;
|
|
329
|
+
if (opName.startsWith("ble."))
|
|
330
|
+
result.usesBle = true;
|
|
331
|
+
if (opName.startsWith("preferences."))
|
|
332
|
+
result.usesPreferences = true;
|
|
333
|
+
if (opName.startsWith("random."))
|
|
334
|
+
result.usesRandom = true;
|
|
335
|
+
if (opName.startsWith("fs."))
|
|
336
|
+
result.usesFS = true;
|
|
337
|
+
if (opName.startsWith("mdns."))
|
|
338
|
+
result.usesMdns = true;
|
|
339
|
+
if (opName.startsWith("mqtt."))
|
|
340
|
+
result.usesMqtt = true;
|
|
341
|
+
if (opName.startsWith("ota."))
|
|
342
|
+
result.usesOta = true;
|
|
343
|
+
if (opName.startsWith("temp."))
|
|
344
|
+
result.usesTemp = true;
|
|
345
|
+
if (opName.startsWith("hwtimer."))
|
|
346
|
+
result.usesHwtimer = true;
|
|
347
|
+
if (opName.startsWith("capacitive."))
|
|
348
|
+
result.usesCapacitive = true;
|
|
349
|
+
if (opName.startsWith("worker."))
|
|
350
|
+
result.usesWorker = true;
|
|
351
|
+
}
|
|
352
|
+
break;
|
|
195
353
|
}
|
|
196
354
|
}
|
|
197
355
|
/**
|
|
@@ -218,6 +376,11 @@ function analyzeStatement(statement, result, strategy) {
|
|
|
218
376
|
if (statement.callee === "millis" || statement.callee === "delay") {
|
|
219
377
|
result.usesMillis = true;
|
|
220
378
|
}
|
|
379
|
+
// Awaited HAL wait markers become async state-machine poll states that
|
|
380
|
+
// arm deadlines via currentTimeMillis().
|
|
381
|
+
if (statement.callee === "__WIFI_WAIT__" || statement.callee === "__HTTP_WAIT__" || statement.callee === "__HAL_WAIT__") {
|
|
382
|
+
result.usesMillis = true;
|
|
383
|
+
}
|
|
221
384
|
// Namespace-qualified polyfill entry points used as bare call statements
|
|
222
385
|
// (e.g. `Timing.delay(5);`). The expression-level analyzer (case
|
|
223
386
|
// "method-call") already checks these prefixes, but a statement-form call
|
|
@@ -234,6 +397,47 @@ function analyzeStatement(statement, result, strategy) {
|
|
|
234
397
|
if (statement.callee.startsWith("WDT.") || statement.callee === "WDT") {
|
|
235
398
|
result.usesWDT = true;
|
|
236
399
|
}
|
|
400
|
+
// Native AVR peripheral usage from statement-form calls. tone()/noTone()
|
|
401
|
+
// are bare Arduino-API calls; console.* / Serial.* drive UART; the
|
|
402
|
+
// namespace prefixes mirror the method-call checks above.
|
|
403
|
+
if (statement.callee === "tone" || statement.callee === "noTone") {
|
|
404
|
+
result.usesTone = true;
|
|
405
|
+
}
|
|
406
|
+
// console.* is NOT UART: on ESP-IDF it lowers to printf, on Arduino to
|
|
407
|
+
// Serial via hasConsoleCalls. Only real UART HAL / Serial peripheral
|
|
408
|
+
// usage should gate the uart shim (avoids unused __tc_uart*_init).
|
|
409
|
+
if (statement.callee.startsWith("_uart_")) {
|
|
410
|
+
result.usesUart = true;
|
|
411
|
+
}
|
|
412
|
+
if (statement.callee.startsWith("Serial.") || statement.callee === "Serial") {
|
|
413
|
+
result.usesUart = true;
|
|
414
|
+
}
|
|
415
|
+
if (statement.callee.startsWith("SPI.") || statement.callee === "SPI") {
|
|
416
|
+
result.usesSPI = true;
|
|
417
|
+
}
|
|
418
|
+
if (statement.callee.startsWith("Wire.") || statement.callee === "Wire") {
|
|
419
|
+
result.usesI2C = true;
|
|
420
|
+
}
|
|
421
|
+
if (statement.callee.startsWith("EEPROM.") || statement.callee === "EEPROM") {
|
|
422
|
+
result.usesEEPROM = true;
|
|
423
|
+
}
|
|
424
|
+
// Statement-form map()/constrain() mirror the method-call checks above.
|
|
425
|
+
if (statement.callee === "map")
|
|
426
|
+
result.usesMap = true;
|
|
427
|
+
if (statement.callee === "constrain")
|
|
428
|
+
result.usesConstrain = true;
|
|
429
|
+
// Statement-form setInterval/setTimeout mirror the expression-level
|
|
430
|
+
// timer-count (analyzeExpression). A top-level `setInterval(...)` call
|
|
431
|
+
// stays a `call` statement (never becomes a method-call expression — see
|
|
432
|
+
// the comment above re: Timing./Num./WDT.), so without this mirror the
|
|
433
|
+
// timer polyfill gate (timerCallCount) stays 0 and __tc_setInterval is
|
|
434
|
+
// emitted as a call but never defined (demo: "'__tc_setInterval' was not
|
|
435
|
+
// declared in this scope"). The arrow-callback hoister renames the callee
|
|
436
|
+
// to __tc_setInterval/__tc_setTimeout, so check both pre- and post-rename.
|
|
437
|
+
if (statement.callee === "setInterval" || statement.callee === "setTimeout"
|
|
438
|
+
|| statement.callee === "__tc_setInterval" || statement.callee === "__tc_setTimeout") {
|
|
439
|
+
result.timerCallCount++;
|
|
440
|
+
}
|
|
237
441
|
// The HAL resolver lowers WDT.*/Timing.* namespace calls to bare AVR
|
|
238
442
|
// library functions (WDT.reset() → wdt_reset(), Timing.delay() → delay(),
|
|
239
443
|
// Timing.millis() → millis()). When that happens the `WDT.`/`Timing.`
|
|
@@ -261,6 +465,27 @@ function analyzeStatement(statement, result, strategy) {
|
|
|
261
465
|
}
|
|
262
466
|
}
|
|
263
467
|
}
|
|
468
|
+
// Timer polyfill sizing for __EMIT__ statements (callee "__EMIT__"):
|
|
469
|
+
// Timing.setInterval/Timing.setTimeout can lower to an __EMIT__ call whose
|
|
470
|
+
// single string arg is the raw C++ payload, e.g.
|
|
471
|
+
// `return __tc_setInterval(<cb>, 500)`. The callee-based timer counter
|
|
472
|
+
// above only fires for bare setInterval/__tc_setInterval calls, so without
|
|
473
|
+
// this the timer_methods polyfill is gated off and the link fails:
|
|
474
|
+
// "'__tc_setInterval' was not declared in this scope". Count call sites
|
|
475
|
+
// embedded in the raw payload. (The hal-op raw-code path has a parallel
|
|
476
|
+
// counter below.)
|
|
477
|
+
if (statement.callee === "__EMIT__") {
|
|
478
|
+
for (const arg of statement.args) {
|
|
479
|
+
if (arg && arg.kind === "string" && typeof arg.value === "string") {
|
|
480
|
+
const matches = arg.value.match(/__tc_setInterval\s*\(/g);
|
|
481
|
+
if (matches)
|
|
482
|
+
result.timerCallCount += matches.length;
|
|
483
|
+
const timeouts = arg.value.match(/__tc_setTimeout\s*\(/g);
|
|
484
|
+
if (timeouts)
|
|
485
|
+
result.timerCallCount += timeouts.length;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
264
489
|
for (const arg of statement.args) {
|
|
265
490
|
analyzeExpression(arg, result, strategy);
|
|
266
491
|
}
|
|
@@ -357,6 +582,122 @@ function analyzeStatement(statement, result, strategy) {
|
|
|
357
582
|
}
|
|
358
583
|
break;
|
|
359
584
|
case "hal-op":
|
|
585
|
+
// Structured HAL ops carry a typed operation name (e.g. "spi.begin",
|
|
586
|
+
// "i2c.read_byte", "tone.play", "uart.write") rather than raw code.
|
|
587
|
+
// Detect peripheral usage here so framework-avr's driver shims can be
|
|
588
|
+
// gated on actual use. Without this, SPI0.begin() → spi.begin hal-op
|
|
589
|
+
// would be invisible (no raw code to scan) and the SPI driver would
|
|
590
|
+
// always be emitted. Same blind spot the wdt.* fix below the raw-code
|
|
591
|
+
// block addresses for the watchdog.
|
|
592
|
+
if (statement.operation && typeof statement.operation.operation === "string") {
|
|
593
|
+
const opName = statement.operation.operation;
|
|
594
|
+
if (opName.startsWith("spi."))
|
|
595
|
+
result.usesSPI = true;
|
|
596
|
+
if (opName.startsWith("i2c."))
|
|
597
|
+
result.usesI2C = true;
|
|
598
|
+
if (opName.startsWith("tone."))
|
|
599
|
+
result.usesTone = true;
|
|
600
|
+
if (opName.startsWith("uart."))
|
|
601
|
+
result.usesUart = true;
|
|
602
|
+
// Display HAL ops (display.init from ui.mount, display.flush per frame)
|
|
603
|
+
// always imply GPIO usage (CS/DC/RST pins). Native SPI/I2C display
|
|
604
|
+
// adapters emit their own transport #includes (driver/spi_master.h,
|
|
605
|
+
// driver/i2c_master.h) — we don't force usesSPI/usesI2C here because
|
|
606
|
+
// that would pull headers even for non-display SPI/I2C code, and the
|
|
607
|
+
// display's transport type can't be determined from the op alone.
|
|
608
|
+
if (opName.startsWith("display.")) {
|
|
609
|
+
result.usesGPIO = true;
|
|
610
|
+
result.usesDisplay = true;
|
|
611
|
+
}
|
|
612
|
+
// ESP32 peripheral usage — framework-esp32 gates IDF driver blocks
|
|
613
|
+
// and forced includes on these. No-op for other frameworks (their
|
|
614
|
+
// shimLines emit no CUTTLEFISH_* blocks with these marker names).
|
|
615
|
+
if (opName.startsWith("gpio."))
|
|
616
|
+
result.usesGPIO = true;
|
|
617
|
+
if (opName === "gpio.read")
|
|
618
|
+
result.usesDigitalRead = true;
|
|
619
|
+
if (opName.startsWith("pwm."))
|
|
620
|
+
result.usesPWM = true;
|
|
621
|
+
if (opName.startsWith("rmt."))
|
|
622
|
+
result.usesRmt = true;
|
|
623
|
+
if (opName.startsWith("adc."))
|
|
624
|
+
result.usesADC = true;
|
|
625
|
+
if (opName.startsWith("dac."))
|
|
626
|
+
result.usesDAC = true;
|
|
627
|
+
if (opName.startsWith("power."))
|
|
628
|
+
result.usesPower = true;
|
|
629
|
+
if (opName.startsWith("wdt."))
|
|
630
|
+
result.usesWdt = true;
|
|
631
|
+
// Safety HAL ops (@typecad/safety) — map each op to the
|
|
632
|
+
// __tc_safety_* polyfill helper it triggers, so the mode-table and
|
|
633
|
+
// voter polyfills survive tree-shaking when actually used. Matches
|
|
634
|
+
// the resolveSafetyOp output naming. Cast through string because the
|
|
635
|
+
// HALOpIR operation union is closed and doesn't include safety.* ops
|
|
636
|
+
// (those are owned by the optional @typecad/safety package).
|
|
637
|
+
const safetyOpName = opName;
|
|
638
|
+
if (safetyOpName === "safety.record_pin_mode") {
|
|
639
|
+
result.usedPolyfillHelpers.add("__tc_safety_record_pin_mode");
|
|
640
|
+
}
|
|
641
|
+
if (safetyOpName === "safety.read_safe") {
|
|
642
|
+
result.usedPolyfillHelpers.add("__tc_safety_read_safe");
|
|
643
|
+
}
|
|
644
|
+
if (safetyOpName === "safety.write_verify") {
|
|
645
|
+
result.usedPolyfillHelpers.add("__tc_safety_write_verify");
|
|
646
|
+
}
|
|
647
|
+
if (opName.startsWith("interrupt."))
|
|
648
|
+
result.usesInterrupts = true;
|
|
649
|
+
if (opName.startsWith("pulse."))
|
|
650
|
+
result.usesPulse = true;
|
|
651
|
+
if (opName.startsWith("shift."))
|
|
652
|
+
result.usesShift = true;
|
|
653
|
+
if (opName.startsWith("wifi.")) {
|
|
654
|
+
result.usesWifi = true;
|
|
655
|
+
if (opName === "wifi.connect" || opName === "wifi.connect_start" || opName === "wifi.disconnect") {
|
|
656
|
+
result.usesWifiConnect = true;
|
|
657
|
+
if (opName === "wifi.connect")
|
|
658
|
+
result.usesWifiConnectBlocking = true;
|
|
659
|
+
}
|
|
660
|
+
else if (opName.startsWith("wifi.scan"))
|
|
661
|
+
result.usesWifiScan = true;
|
|
662
|
+
else if (opName === "wifi.status" || opName === "wifi.local_ip" || opName === "wifi.rssi" || opName === "wifi.mac")
|
|
663
|
+
result.usesWifiQuery = true;
|
|
664
|
+
else if (opName === "wifi.set_hostname")
|
|
665
|
+
result.usesWifiConfig = true;
|
|
666
|
+
}
|
|
667
|
+
if (opName.startsWith("http."))
|
|
668
|
+
result.usesHttp = true;
|
|
669
|
+
if (opName.startsWith("ble."))
|
|
670
|
+
result.usesBle = true;
|
|
671
|
+
if (opName.startsWith("preferences."))
|
|
672
|
+
result.usesPreferences = true;
|
|
673
|
+
if (opName.startsWith("random."))
|
|
674
|
+
result.usesRandom = true;
|
|
675
|
+
if (opName.startsWith("fs."))
|
|
676
|
+
result.usesFS = true;
|
|
677
|
+
if (opName.startsWith("mdns."))
|
|
678
|
+
result.usesMdns = true;
|
|
679
|
+
if (opName.startsWith("mqtt."))
|
|
680
|
+
result.usesMqtt = true;
|
|
681
|
+
if (opName.startsWith("ota."))
|
|
682
|
+
result.usesOta = true;
|
|
683
|
+
if (opName.startsWith("temp."))
|
|
684
|
+
result.usesTemp = true;
|
|
685
|
+
if (opName.startsWith("hwtimer."))
|
|
686
|
+
result.usesHwtimer = true;
|
|
687
|
+
if (opName.startsWith("capacitive."))
|
|
688
|
+
result.usesCapacitive = true;
|
|
689
|
+
if (opName.startsWith("worker."))
|
|
690
|
+
result.usesWorker = true;
|
|
691
|
+
// Timing HAL ops (timing.delay/millis/micros) carry a typed operation
|
|
692
|
+
// name, not raw code, so the regex scans below miss them. Mirror the
|
|
693
|
+
// raw-code timing detection here so usesMillis/usesTiming (and thus
|
|
694
|
+
// usesNativeTiming) fire for `delay()`/`millis()` on AVR.
|
|
695
|
+
if (opName === "timing.delay" || opName === "timing.delay_microseconds"
|
|
696
|
+
|| opName === "timing.millis" || opName === "timing.micros") {
|
|
697
|
+
result.usesTiming = true;
|
|
698
|
+
result.usesMillis = true;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
360
701
|
// Scan raw C++ code in HAL ops for polyfill helper usage
|
|
361
702
|
if (statement.operation && statement.operation.operation === "raw" && typeof statement.operation.code === "string") {
|
|
362
703
|
const code = statement.operation.code;
|
|
@@ -384,6 +725,38 @@ function analyzeStatement(statement, result, strategy) {
|
|
|
384
725
|
result.usesTiming = true;
|
|
385
726
|
result.usesMillis = true;
|
|
386
727
|
}
|
|
728
|
+
// Timer polyfill sizing — `Timing.setInterval(...)` / `Timing.setTimeout(...)`
|
|
729
|
+
// are resolved by the HAL method resolver, which reads TimingClass's
|
|
730
|
+
// rawCpp body and emits a raw hal-op whose code is e.g.
|
|
731
|
+
// `return __tc_setInterval(<cb>, 500)`. The callee-based counter below
|
|
732
|
+
// (statement.callee === "__tc_setInterval") never fires for these — the
|
|
733
|
+
// statement is a hal-op, not a bare timer call — so without counting
|
|
734
|
+
// here the timer_methods polyfill is gated off (timerCallCount == 0)
|
|
735
|
+
// and the link fails: "'__tc_setInterval' was not declared in this
|
|
736
|
+
// scope". Count each __tc_setInterval/__tc_setTimeout call site in the
|
|
737
|
+
// raw code. The optional `return ` prefix is emitted by value-returning
|
|
738
|
+
// HAL methods (statement context), so match with or without it.
|
|
739
|
+
const intervalSites = code.match(/__tc_setInterval\s*\(/g);
|
|
740
|
+
if (intervalSites)
|
|
741
|
+
result.timerCallCount += intervalSites.length;
|
|
742
|
+
const timeoutSites = code.match(/__tc_setTimeout\s*\(/g);
|
|
743
|
+
if (timeoutSites)
|
|
744
|
+
result.timerCallCount += timeoutSites.length;
|
|
745
|
+
// Native AVR peripheral usage inside raw hal-op code (e.g. the
|
|
746
|
+
// EEPROM namespace lowers to `EEPROM.write(...)` in a raw hal-op;
|
|
747
|
+
// Serial/SPI/Wire may appear as lowered library calls too).
|
|
748
|
+
if (/\bEEPROM\b/.test(code) || /\beeprom_(read|write|update)_byte\b/.test(code)) {
|
|
749
|
+
result.usesEEPROM = true;
|
|
750
|
+
}
|
|
751
|
+
if (/\bSerial\b/.test(code)) {
|
|
752
|
+
result.usesUart = true;
|
|
753
|
+
}
|
|
754
|
+
if (/\bSPI\b/.test(code)) {
|
|
755
|
+
result.usesSPI = true;
|
|
756
|
+
}
|
|
757
|
+
if (/\bWire\b/.test(code)) {
|
|
758
|
+
result.usesI2C = true;
|
|
759
|
+
}
|
|
387
760
|
}
|
|
388
761
|
break;
|
|
389
762
|
case "update":
|
|
@@ -431,10 +804,53 @@ export function analyzeProgram(program, strategy) {
|
|
|
431
804
|
usesTiming: false,
|
|
432
805
|
usesWDT: false,
|
|
433
806
|
usesStrPtr: false,
|
|
807
|
+
usesUart: false,
|
|
808
|
+
usesSPI: false,
|
|
809
|
+
usesI2C: false,
|
|
810
|
+
usesEEPROM: false,
|
|
811
|
+
usesTone: false,
|
|
812
|
+
usesMap: false,
|
|
813
|
+
usesConstrain: false,
|
|
814
|
+
usesNativeTiming: false,
|
|
434
815
|
hasSerialBegin: false,
|
|
435
816
|
hasGenerators: false,
|
|
436
817
|
usesStdMap: false,
|
|
437
818
|
timerCallCount: 0,
|
|
819
|
+
usesGPIO: false,
|
|
820
|
+
usesPWM: false,
|
|
821
|
+
usesRmt: false,
|
|
822
|
+
usesADC: false,
|
|
823
|
+
usesDAC: false,
|
|
824
|
+
usesPower: false,
|
|
825
|
+
usesWdt: false,
|
|
826
|
+
usesInterrupts: false,
|
|
827
|
+
usesPulse: false,
|
|
828
|
+
usesShift: false,
|
|
829
|
+
usesWifi: false,
|
|
830
|
+
usesWifiConnect: false,
|
|
831
|
+
usesWifiConnectBlocking: false,
|
|
832
|
+
usesWifiQuery: false,
|
|
833
|
+
usesWifiScan: false,
|
|
834
|
+
usesWifiConfig: false,
|
|
835
|
+
usesHttp: false,
|
|
836
|
+
usesBle: false,
|
|
837
|
+
usesPreferences: false,
|
|
838
|
+
usesRandom: false,
|
|
839
|
+
usesFS: false,
|
|
840
|
+
usesMdns: false,
|
|
841
|
+
usesMqtt: false,
|
|
842
|
+
usesOta: false,
|
|
843
|
+
usesTemp: false,
|
|
844
|
+
usesHwtimer: false,
|
|
845
|
+
usesCapacitive: false,
|
|
846
|
+
usesWorker: false,
|
|
847
|
+
usesChrono: false,
|
|
848
|
+
usesSet: false,
|
|
849
|
+
usesAlgorithm: false,
|
|
850
|
+
usesCstdio: false,
|
|
851
|
+
usesDigitalRead: false,
|
|
852
|
+
usesDisplay: false,
|
|
853
|
+
usesHalt: false,
|
|
438
854
|
};
|
|
439
855
|
// Analyze type aliases
|
|
440
856
|
for (const typeAlias of program.typeAliases) {
|
|
@@ -585,5 +1001,18 @@ export function analyzeProgram(program, strategy) {
|
|
|
585
1001
|
if (loweredConsoleInCallback()) {
|
|
586
1002
|
result.hasConsoleCalls = true;
|
|
587
1003
|
}
|
|
1004
|
+
// Derive the comprehensive native-timing gate for the AVR millis() Timer0
|
|
1005
|
+
// ISR. The ISR is needed whenever the program touches the soft clock
|
|
1006
|
+
// directly (millis/delay/micros) OR has a hidden consumer: setInterval/
|
|
1007
|
+
// setTimeout (the scheduler polls millis), or async functions (the runtime
|
|
1008
|
+
// polls millis). The per-frame UI tick is injected by the emitter, not
|
|
1009
|
+
// present in user source — the setup emitter ORs entryHasUI() in at the
|
|
1010
|
+
// consume site. Without this gate, every AVR program pulled in the Timer0
|
|
1011
|
+
// ISR even when it never uses timing.
|
|
1012
|
+
const hasAsync = program.functions.some(fn => fn.isAsync);
|
|
1013
|
+
result.usesNativeTiming = result.usesMillis
|
|
1014
|
+
|| result.usesTiming
|
|
1015
|
+
|| result.timerCallCount > 0
|
|
1016
|
+
|| hasAsync;
|
|
588
1017
|
return result;
|
|
589
1018
|
}
|
|
@@ -4,4 +4,4 @@ import type { BoardConstants } from './board-resolver.js';
|
|
|
4
4
|
/**
|
|
5
5
|
* Validate that pins used with inputPullDown() support pulldown on this board.
|
|
6
6
|
*/
|
|
7
|
-
export declare function validatePulldownSupport(usage: PeripheralUsage, boardConstants: BoardConstants | undefined): Diagnostic[];
|
|
7
|
+
export declare function validatePulldownSupport(usage: PeripheralUsage, boardConstants: BoardConstants | undefined, filePath: string): Diagnostic[];
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
/**
|
|
8
8
|
* Validate that pins used with inputPullDown() support pulldown on this board.
|
|
9
9
|
*/
|
|
10
|
-
export function validatePulldownSupport(usage, boardConstants) {
|
|
10
|
+
export function validatePulldownSupport(usage, boardConstants, filePath) {
|
|
11
11
|
const diagnostics = [];
|
|
12
12
|
if (usage.inputPulldownPins.size === 0) {
|
|
13
13
|
return diagnostics;
|
|
@@ -22,6 +22,7 @@ export function validatePulldownSupport(usage, boardConstants) {
|
|
|
22
22
|
diagnostics.push({
|
|
23
23
|
code: 'pulldown-not-supported',
|
|
24
24
|
message: `${pinName ?? `pin ${pinNumber}`} does not support hardware pulldown${arch ? ` on ${arch.toUpperCase()} boards` : ''}. Use ${pinName ?? 'pin'}.asInput() or ${pinName ?? 'pin'}.inputPullUp() instead.`,
|
|
25
|
+
filePath,
|
|
25
26
|
source: 'pulldown-validation',
|
|
26
27
|
severity: 'error',
|
|
27
28
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { Diagnostic } from '../types.js';
|
|
2
2
|
import type { BoardConstants } from './board-resolver.js';
|
|
3
3
|
import type { PeripheralUsage } from './peripheral-usage.js';
|
|
4
|
-
export declare function validatePWMTimerSharing(usage: PeripheralUsage, boardConstants: BoardConstants | undefined): Diagnostic[];
|
|
4
|
+
export declare function validatePWMTimerSharing(usage: PeripheralUsage, boardConstants: BoardConstants | undefined, filePath: string): Diagnostic[];
|
|
@@ -52,7 +52,7 @@ function getPwmTimerPins(boardConstants) {
|
|
|
52
52
|
}
|
|
53
53
|
return Array.from(pinsByIndex.values()).filter(pin => pin.timerId.length > 0);
|
|
54
54
|
}
|
|
55
|
-
export function validatePWMTimerSharing(usage, boardConstants) {
|
|
55
|
+
export function validatePWMTimerSharing(usage, boardConstants, filePath) {
|
|
56
56
|
if (usage.pwmPinsUsed.size < 2 || !boardConstants) {
|
|
57
57
|
return [];
|
|
58
58
|
}
|
|
@@ -85,6 +85,7 @@ export function validatePWMTimerSharing(usage, boardConstants) {
|
|
|
85
85
|
diagnostics.push({
|
|
86
86
|
severity: 'info',
|
|
87
87
|
code: 'pwm-timer-sharing',
|
|
88
|
+
filePath,
|
|
88
89
|
source: 'pwm-timer-sharing',
|
|
89
90
|
message: `${references.join(', ')} share ${timerId}${boardLabel}. Duty cycle can differ per pin, but timer-wide PWM settings are shared across that group. Prefer pins on different timer groups if you need independent PWM timing behavior.`,
|
|
90
91
|
});
|
package/dist/ir/render-expr.js
CHANGED
|
@@ -85,6 +85,17 @@ export function renderExprAsText(expr) {
|
|
|
85
85
|
case "lambda":
|
|
86
86
|
return `/* __lambda__ */`;
|
|
87
87
|
case "method-call": {
|
|
88
|
+
// Structured-receiver path: when the method-call IR carries a receiverExpr
|
|
89
|
+
// (set by the safe.read().ok().fail() chain builder), render the receiver
|
|
90
|
+
// recursively so nested lambda args are NOT flattened into the callee
|
|
91
|
+
// string as /* __lambda__ */ placeholders. The flat callee path below
|
|
92
|
+
// destroys lambdas; this preserves them.
|
|
93
|
+
const receiverExpr = expr.receiverExpr;
|
|
94
|
+
const methodName = expr.methodName;
|
|
95
|
+
if (receiverExpr !== undefined && methodName !== undefined) {
|
|
96
|
+
const argsText = expr.args.map(a => renderExprAsText(a)).join(", ");
|
|
97
|
+
return `${renderExprAsText(receiverExpr)}.${methodName}(${argsText})`;
|
|
98
|
+
}
|
|
88
99
|
const argsText = expr.args.map(a => renderExprAsText(a)).join(", ");
|
|
89
100
|
return `${expr.callee}(${argsText})`;
|
|
90
101
|
}
|
|
@@ -34,6 +34,7 @@ export function analyzeResources(program, strategy) {
|
|
|
34
34
|
diagnostics.push({
|
|
35
35
|
severity: 'warning',
|
|
36
36
|
message: `${pinRef} is configured as ${peripheralName} ${func.role.toUpperCase()}. Using it as GPIO may interfere with communication.\n → ${suggestion}`,
|
|
37
|
+
filePath: program.fileName,
|
|
37
38
|
code: 'peripheral-pin-conflict',
|
|
38
39
|
source: 'resource-analysis',
|
|
39
40
|
});
|
|
@@ -58,6 +59,7 @@ export function analyzeResources(program, strategy) {
|
|
|
58
59
|
severity: 'error',
|
|
59
60
|
message: `Hardware Conflict: Pin '${pinName}' is required by both ${peripheralName} and ${other}.`,
|
|
60
61
|
hint: `Move one of the peripherals to different pins if your board supports remapping, or avoid using both simultaneously.`,
|
|
62
|
+
filePath: program.fileName,
|
|
61
63
|
code: 'peripheral-peripheral-conflict',
|
|
62
64
|
source: 'resource-analysis',
|
|
63
65
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { Diagnostic } from '../types.js';
|
|
2
2
|
import type { BoardConstants } from './board-resolver.js';
|
|
3
3
|
import type { PeripheralUsage } from './peripheral-usage.js';
|
|
4
|
-
export declare function validateTimer0PWMTimingConflict(usage: PeripheralUsage, boardConstants: BoardConstants | undefined): Diagnostic[];
|
|
4
|
+
export declare function validateTimer0PWMTimingConflict(usage: PeripheralUsage, boardConstants: BoardConstants | undefined, filePath: string): Diagnostic[];
|
|
@@ -43,7 +43,7 @@ function getTimer0PwmPins(boardConstants) {
|
|
|
43
43
|
}
|
|
44
44
|
return timer0Pins;
|
|
45
45
|
}
|
|
46
|
-
export function validateTimer0PWMTimingConflict(usage, boardConstants) {
|
|
46
|
+
export function validateTimer0PWMTimingConflict(usage, boardConstants, filePath) {
|
|
47
47
|
if (!usage.timer0 || usage.pwmPinsUsed.size === 0) {
|
|
48
48
|
return [];
|
|
49
49
|
}
|
|
@@ -65,6 +65,7 @@ export function validateTimer0PWMTimingConflict(usage, boardConstants) {
|
|
|
65
65
|
return [{
|
|
66
66
|
severity: 'info',
|
|
67
67
|
code: 'timer0-pwm-timing-conflict',
|
|
68
|
+
filePath,
|
|
68
69
|
source: 'timer0-pwm-timing-conflict',
|
|
69
70
|
message: `${pinReferences.join(', ')} use Timer0 PWM${boardLabel}, and your program also relies on Timer0-backed timing APIs such as delay(), millis(), or micros(). This coupling is common on AVR boards, so prefer non-Timer0 PWM pins when you want PWM behavior isolated from core timing.`,
|
|
70
71
|
}];
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import type { ProgramIR } from '../api/index.js';
|
|
2
2
|
import type { Diagnostic } from '../types.js';
|
|
3
|
+
import type { PlatformStrategy } from '../api/shared/index.js';
|
|
3
4
|
/**
|
|
4
5
|
* Detect blocking delay() calls inside loop()'s body. Emits a warning for
|
|
5
6
|
* each, explaining the cooperative-async alternative.
|
|
7
|
+
*
|
|
8
|
+
* On RTOS targets (ESP-IDF), timing.delay lowers to vTaskDelay which yields
|
|
9
|
+
* the CPU — it does NOT freeze the async queue or UI. Only delayMicroseconds
|
|
10
|
+
* (busy-wait) triggers the warning.
|
|
6
11
|
*/
|
|
7
|
-
export declare function validateBlockingDelayInLoop(program: ProgramIR): Diagnostic[];
|
|
12
|
+
export declare function validateBlockingDelayInLoop(program: ProgramIR, strategy?: PlatformStrategy): Diagnostic[];
|