@typecad/cuttlefish 1.0.0-alpha.3 → 1.0.0-alpha.7
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 +4 -4
- package/dist/api/shared/display-adapter.d.ts +2 -1
- package/dist/api/shared/display-adapter.js +8 -1
- package/dist/api/shared/display-adapters/sdl.js +9 -2
- package/dist/api/shared/display-profile.d.ts +20 -3
- package/dist/api/shared/display-profile.js +21 -6
- package/dist/api/shared/framework-manifest-registry.d.ts +9 -0
- package/dist/api/shared/framework-manifest-registry.js +25 -0
- package/dist/api/shared/framework-manifest.d.ts +462 -0
- package/dist/api/shared/framework-manifest.js +149 -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 +28 -0
- package/dist/api/shared/hal-op-ir.d.ts +427 -1
- package/dist/api/shared/hal-op-ir.js +95 -1
- package/dist/api/shared/index.d.ts +11 -1
- package/dist/api/shared/index.js +14 -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 +9 -0
- package/dist/api/shared/promise-runtime.js +78 -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 +417 -0
- package/dist/cli-utils.d.ts +1 -0
- package/dist/cli-utils.js +3 -1
- package/dist/cli.js +175 -4
- package/dist/config-loader.js +20 -1
- package/dist/config-schema.d.ts +36 -36
- package/dist/create/board-spec.d.ts +4 -4
- package/dist/create/index.d.ts +1 -1
- package/dist/create/index.js +1 -1
- package/dist/create/init-scaffold.d.ts +3 -0
- package/dist/create/init-scaffold.js +74 -2
- package/dist/create/init-templates.d.ts +4 -0
- package/dist/create/init-templates.js +217 -17
- package/dist/create/init-wizard.js +20 -1
- package/dist/emit/cpp-emitter.js +4 -3
- package/dist/emit/emitters/emitter-context.d.ts +5 -0
- package/dist/emit/emitters/function-emitter-impl.js +69 -59
- package/dist/emit/emitters/output-finalizer.d.ts +6 -0
- package/dist/emit/emitters/output-finalizer.js +21 -11
- package/dist/emit/emitters/setup.js +155 -0
- package/dist/emit/emitters/top-level-prep.js +2 -0
- package/dist/emit/emitters/ui-emitter.js +23 -8
- package/dist/emit/expression-renderer.js +10 -1
- package/dist/emit/snprintf-helpers.js +8 -0
- package/dist/emit/statement-renderer.js +13 -0
- package/dist/emit/utils/async-state-machine.js +185 -116
- 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/ir/adc-range-validation.js +26 -25
- package/dist/ir/build-ir.js +5 -1
- package/dist/ir/expression-to-ir.js +57 -6
- package/dist/ir/feature-registry.js +7 -25
- package/dist/ir/hal/hal-emitter.d.ts +5 -2
- package/dist/ir/hal/hal-emitter.js +40 -12
- package/dist/ir/hal/hal-parser.d.ts +6 -0
- package/dist/ir/hal/hal-parser.js +74 -0
- package/dist/ir/hal/hal-plugins.js +571 -0
- package/dist/ir/identifier-collector.js +18 -0
- package/dist/ir/interrupt-analysis.js +8 -3
- 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 +33 -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 +34 -32
- package/dist/ir/pin-mode-validation.js +5 -0
- package/dist/ir/pin-safety.d.ts +1 -1
- package/dist/ir/pin-safety.js +2 -1
- package/dist/ir/program-analysis.d.ts +39 -0
- package/dist/ir/program-analysis.js +192 -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/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 +51 -12
- package/dist/ir/transformers/expressions.js +58 -0
- package/dist/ir/transformers/hal-emit-helpers.js +1 -1
- package/dist/ir/transformers/variables.js +86 -19
- package/dist/ir/try-catch-validation.js +2 -0
- package/dist/ir/type-resolution.js +2 -2
- package/dist/ir/unit-suspicion-validation.js +9 -7
- package/dist/ir/validation-orchestrator.js +9 -7
- 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/idf-discovery.d.ts +7 -0
- package/dist/libdef/idf-discovery.js +59 -0
- package/dist/libdef/registry.js +5 -2
- package/dist/licenses.d.ts +185 -0
- package/dist/licenses.js +963 -0
- package/dist/lint-cache.d.ts +59 -0
- package/dist/lint-cache.js +257 -0
- package/dist/orchestrator/graph-builder.js +6 -2
- package/dist/stores/display-profile-store.d.ts +1 -0
- package/dist/stores/display-profile-store.js +1 -0
- package/dist/testing.d.ts +4 -2
- package/dist/testing.js +4 -2
- package/dist/transpile.d.ts +3 -0
- package/dist/transpile.js +78 -32
- package/dist/types.d.ts +5 -1
- package/dist/ui-hook.d.ts +17 -1
- package/dist/utils/cli.js +71 -1
- package/dist/utils/fs.d.ts +13 -0
- package/dist/utils/fs.js +50 -0
- package/package.json +9 -4
- package/dist/ir/heap-array-validation.d.ts +0 -24
- package/dist/ir/heap-array-validation.js +0 -29
|
@@ -26,7 +26,7 @@ function polyfillDefinitionGuard(definition) {
|
|
|
26
26
|
return null;
|
|
27
27
|
}
|
|
28
28
|
export function emitPreamble(ctx) {
|
|
29
|
-
const { strategy, effectiveEmitMode, program, shimLines, emittedPolyfills,
|
|
29
|
+
const { strategy, effectiveEmitMode, program, shimLines, emittedPolyfills, includes, programAnalysis } = ctx;
|
|
30
30
|
for (const include of dedupe(includes)) {
|
|
31
31
|
appendSourceLineLocal(ctx, `#include ${include}`);
|
|
32
32
|
}
|
|
@@ -73,16 +73,8 @@ export function emitPreamble(ctx) {
|
|
|
73
73
|
}
|
|
74
74
|
appendSourceLineLocal(ctx, "");
|
|
75
75
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
for (const line of classDef.split("\n")) {
|
|
79
|
-
appendSourceLineLocal(ctx, line);
|
|
80
|
-
}
|
|
81
|
-
appendSourceLineLocal(ctx, "");
|
|
82
|
-
appendSourceLineLocal(ctx, instanceDecl);
|
|
83
|
-
appendSourceLineLocal(ctx, "");
|
|
84
|
-
}
|
|
85
|
-
}
|
|
76
|
+
// Async task classes are emitted later (emitAsyncTaskClasses) AFTER top-level
|
|
77
|
+
// globals so references like WIFI_SSID inside the state machine compile.
|
|
86
78
|
if (strategy.needsVectorOverload() && hasConsoleCalls(program, strategy) && (programAnalysis.usesVectorTypes || programAnalysis.hasArrayInObjectLiteral)) {
|
|
87
79
|
appendSourceLineLocal(ctx, "template <typename T>");
|
|
88
80
|
appendSourceLineLocal(ctx, "std::ostream& operator<<(std::ostream& os, const std::vector<T>& values)");
|
|
@@ -136,6 +128,24 @@ export function emitPreamble(ctx) {
|
|
|
136
128
|
appendSourceLineLocal(ctx, "");
|
|
137
129
|
}
|
|
138
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Emit cooperative async state-machine classes. Must run AFTER top-level
|
|
133
|
+
* globals (emitTypeDeclarations) so identifiers like WIFI_SSID referenced
|
|
134
|
+
* from task bodies are already declared.
|
|
135
|
+
*/
|
|
136
|
+
export function emitAsyncTaskClasses(ctx) {
|
|
137
|
+
const { asyncTaskClasses } = ctx;
|
|
138
|
+
if (asyncTaskClasses.length === 0)
|
|
139
|
+
return;
|
|
140
|
+
for (const { classDef, instanceDecl } of asyncTaskClasses) {
|
|
141
|
+
for (const line of classDef.split("\n")) {
|
|
142
|
+
appendSourceLineLocal(ctx, line);
|
|
143
|
+
}
|
|
144
|
+
appendSourceLineLocal(ctx, "");
|
|
145
|
+
appendSourceLineLocal(ctx, instanceDecl);
|
|
146
|
+
appendSourceLineLocal(ctx, "");
|
|
147
|
+
}
|
|
148
|
+
}
|
|
139
149
|
export function finalizeOutput(ctx) {
|
|
140
150
|
const { program, strategy, options, effectiveEmitMode, isNpmPackage, baseName, includes, shimLines, emittedPolyfills } = ctx;
|
|
141
151
|
const outDir = options.outDir;
|
|
@@ -31,6 +31,80 @@ function filterShimBlock(lines, startMarker, endMarker) {
|
|
|
31
31
|
}
|
|
32
32
|
return filtered;
|
|
33
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Detect whether a program uses the watchdog timer. The HAL resolver lowers
|
|
36
|
+
* `WDT.enable/reset/disable` calls to structured `wdt.*` hal-ops, which the
|
|
37
|
+
* setup emitter then renders as bare `wdt_enable/wdt_reset/wdt_disable` calls
|
|
38
|
+
* (or constant-folded `wdt_enable(WDTO_*)` macros). All of those require
|
|
39
|
+
* `<avr/wdt.h>`, so the include is gated on this check instead of being forced
|
|
40
|
+
* into every AVR program.
|
|
41
|
+
*
|
|
42
|
+
* `programAnalysis.usesWDT` is NOT used here: it scans `WDT.`-prefixed
|
|
43
|
+
* callees and raw-code hal-ops, but the structured `wdt.*` ops carry a typed
|
|
44
|
+
* operation name with no raw code, so the analysis misses them.
|
|
45
|
+
*
|
|
46
|
+
* The recursion mirrors `collectStatementIdentifiers` (identifier-collector.ts)
|
|
47
|
+
* — the canonical IR walker — so every compound statement shape is covered.
|
|
48
|
+
*/
|
|
49
|
+
function programUsesWdt(program) {
|
|
50
|
+
const visitStatement = (stmt) => {
|
|
51
|
+
if (stmt.kind === "hal-op") {
|
|
52
|
+
const opName = stmt.operation?.operation;
|
|
53
|
+
if (typeof opName === "string" && opName.startsWith("wdt."))
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
switch (stmt.kind) {
|
|
57
|
+
case "block":
|
|
58
|
+
case "labeled":
|
|
59
|
+
return visit(stmt.body);
|
|
60
|
+
case "if":
|
|
61
|
+
return visit(stmt.thenBranch) || visit(stmt.elseBranch ?? []);
|
|
62
|
+
case "for":
|
|
63
|
+
return visit(stmt.body)
|
|
64
|
+
|| (stmt.initializer ? visitStatement(stmt.initializer) : false);
|
|
65
|
+
case "while":
|
|
66
|
+
case "do_while":
|
|
67
|
+
case "for_of":
|
|
68
|
+
case "for_in":
|
|
69
|
+
return visit(stmt.body);
|
|
70
|
+
case "switch":
|
|
71
|
+
return stmt.cases.some((c) => visit(c.body ?? []));
|
|
72
|
+
case "try":
|
|
73
|
+
return visit(stmt.tryBlock) || visit(stmt.catchBlock ?? []) || visit(stmt.finallyBlock ?? []);
|
|
74
|
+
default:
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
const visit = (statements) => {
|
|
79
|
+
if (!statements)
|
|
80
|
+
return false;
|
|
81
|
+
for (const stmt of statements) {
|
|
82
|
+
if (visitStatement(stmt))
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
};
|
|
87
|
+
if (visit(program.topLevelStatements))
|
|
88
|
+
return true;
|
|
89
|
+
for (const fn of program.functions) {
|
|
90
|
+
if (visit(fn.statements))
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
for (const cls of program.classes) {
|
|
94
|
+
for (const m of cls.methods)
|
|
95
|
+
if (visit(m.statements))
|
|
96
|
+
return true;
|
|
97
|
+
for (const g of cls.getters)
|
|
98
|
+
if (visit(g.statements))
|
|
99
|
+
return true;
|
|
100
|
+
for (const s of cls.setters)
|
|
101
|
+
if (visit(s.statements))
|
|
102
|
+
return true;
|
|
103
|
+
if (cls.constructor && visit(cls.constructor.statements))
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
34
108
|
/**
|
|
35
109
|
* Extract a `#ifndef MACRO ... #endif` include-guard block from a shim line
|
|
36
110
|
* list. Used to emit just the macro definition (e.g. CUTTLEFISH_UNDEFINED) in
|
|
@@ -118,6 +192,14 @@ export function buildEmitterContext(program, options) {
|
|
|
118
192
|
})();
|
|
119
193
|
const programAnalysis = analyzeProgram(program, strategy);
|
|
120
194
|
if (options.platformContext) {
|
|
195
|
+
// The UI runtime's per-frame tick calls millis() (injected by the emitter,
|
|
196
|
+
// not present in user source), so a mounted UI needs the native millis ISR
|
|
197
|
+
// even when usesNativeTiming is false. OR entryHasUI() into the flag the
|
|
198
|
+
// AVR strategy reads when gating native_millis, so framework-avr doesn't
|
|
199
|
+
// drop the Timer0 ISR from a UI program.
|
|
200
|
+
if (!programAnalysis.usesNativeTiming && entryHasUI()) {
|
|
201
|
+
programAnalysis.usesNativeTiming = true;
|
|
202
|
+
}
|
|
121
203
|
options.platformContext.analysis = programAnalysis;
|
|
122
204
|
if (!options.platformContext.architecture && program.boardConstants) {
|
|
123
205
|
options.platformContext.architecture = program.boardConstants.get("architecture");
|
|
@@ -223,6 +305,68 @@ export function buildEmitterContext(program, options) {
|
|
|
223
305
|
if (!programAnalysis.usesStrPtr) {
|
|
224
306
|
shimLines = filterShimBlock(shimLines, '#ifndef CUTTLEFISH_STR_BUF_SIZE', 'inline size_t (strlen)(const __tc_str_ptr& s) { return ::strlen(s.buf); }');
|
|
225
307
|
}
|
|
308
|
+
// framework-avr native peripheral driver shims. Each block carries a
|
|
309
|
+
// CUTTLEFISH_*_BEGIN/END marker pair; strip the ones the program doesn't
|
|
310
|
+
// use. framework-arduino's shimLines contain none of these markers, so
|
|
311
|
+
// these filters are no-ops there. The strategies also self-gate on the
|
|
312
|
+
// same flags; this is the defensive backstop (mirrors how usesWDT/etc.
|
|
313
|
+
// backstop the strategy-side gating above).
|
|
314
|
+
if (!programAnalysis.usesUart) {
|
|
315
|
+
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_UART_BEGIN', '// CUTTLEFISH_UART_END');
|
|
316
|
+
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_UART_EXT_BEGIN', '// CUTTLEFISH_UART_EXT_END');
|
|
317
|
+
}
|
|
318
|
+
if (!programAnalysis.usesSPI) {
|
|
319
|
+
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_SPI_BEGIN', '// CUTTLEFISH_SPI_END');
|
|
320
|
+
}
|
|
321
|
+
if (!programAnalysis.usesI2C) {
|
|
322
|
+
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_TWI_BEGIN', '// CUTTLEFISH_TWI_END');
|
|
323
|
+
}
|
|
324
|
+
if (!programAnalysis.usesEEPROM) {
|
|
325
|
+
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_EEPROM_BEGIN', '// CUTTLEFISH_EEPROM_END');
|
|
326
|
+
}
|
|
327
|
+
if (!programAnalysis.usesTone) {
|
|
328
|
+
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_TONE_BEGIN', '// CUTTLEFISH_TONE_END');
|
|
329
|
+
}
|
|
330
|
+
// framework-esp32 native peripheral driver shims. Same defensive-backstop
|
|
331
|
+
// pattern as the UART/SPI/TWI/EEPROM/tone blocks above. framework-arduino's
|
|
332
|
+
// and framework-avr's shimLines contain none of these markers, so these
|
|
333
|
+
// filters are no-ops there.
|
|
334
|
+
if (!programAnalysis.usesGPIO) {
|
|
335
|
+
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_GPIO_BEGIN', '// CUTTLEFISH_GPIO_END');
|
|
336
|
+
}
|
|
337
|
+
if (!programAnalysis.usesPWM) {
|
|
338
|
+
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_PWM_BEGIN', '// CUTTLEFISH_PWM_END');
|
|
339
|
+
}
|
|
340
|
+
if (!programAnalysis.usesADC) {
|
|
341
|
+
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_ADC_BEGIN', '// CUTTLEFISH_ADC_END');
|
|
342
|
+
}
|
|
343
|
+
if (!programAnalysis.usesDAC) {
|
|
344
|
+
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_DAC_BEGIN', '// CUTTLEFISH_DAC_END');
|
|
345
|
+
}
|
|
346
|
+
if (!programAnalysis.usesPower) {
|
|
347
|
+
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_POWER_BEGIN', '// CUTTLEFISH_POWER_END');
|
|
348
|
+
}
|
|
349
|
+
if (!programAnalysis.usesWdt) {
|
|
350
|
+
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_WDT_BEGIN', '// CUTTLEFISH_WDT_END');
|
|
351
|
+
}
|
|
352
|
+
if (!programAnalysis.usesInterrupts) {
|
|
353
|
+
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_INTR_BEGIN', '// CUTTLEFISH_INTR_END');
|
|
354
|
+
}
|
|
355
|
+
if (!programAnalysis.usesPulse) {
|
|
356
|
+
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_PULSE_BEGIN', '// CUTTLEFISH_PULSE_END');
|
|
357
|
+
}
|
|
358
|
+
if (!programAnalysis.usesShift) {
|
|
359
|
+
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_SHIFT_BEGIN', '// CUTTLEFISH_SHIFT_END');
|
|
360
|
+
}
|
|
361
|
+
if (!programAnalysis.usesWifi) {
|
|
362
|
+
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_WIFI_BEGIN', '// CUTTLEFISH_WIFI_END');
|
|
363
|
+
}
|
|
364
|
+
if (!programAnalysis.usesHttp) {
|
|
365
|
+
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_HTTP_BEGIN', '// CUTTLEFISH_HTTP_END');
|
|
366
|
+
}
|
|
367
|
+
if (!programAnalysis.usesPreferences) {
|
|
368
|
+
shimLines = filterShimBlock(shimLines, '// CUTTLEFISH_PREFERENCES_BEGIN', '// CUTTLEFISH_PREFERENCES_END');
|
|
369
|
+
}
|
|
226
370
|
profileDiagnostics = [...strategy.profileDiagnostics(program, options.platformContext)];
|
|
227
371
|
}
|
|
228
372
|
for (const imported of program.imports) {
|
|
@@ -745,6 +889,17 @@ export function buildEmitterContext(program, options) {
|
|
|
745
889
|
if (stringEnumNames.size > 0) {
|
|
746
890
|
includes.push(strategy.cstringHeader());
|
|
747
891
|
}
|
|
892
|
+
// `<avr/wdt.h>` is only needed when the program actually uses the watchdog.
|
|
893
|
+
// The HAL resolver lowers WDT.enable/reset/disable to bare wdt_*() calls
|
|
894
|
+
// (or constant-folded wdt_enable(WDTO_*) macros), all of which require the
|
|
895
|
+
// header. Detect the structured `wdt.*` hal-ops directly here rather than
|
|
896
|
+
// relying on programAnalysis.usesWDT (which misses these ops — they carry
|
|
897
|
+
// a typed operation name, not raw code the analysis scans) or on a forced
|
|
898
|
+
// include in the AVR profile (which leaked the header into every AVR
|
|
899
|
+
// program, even ones that never touch the watchdog like `led.toggle()`).
|
|
900
|
+
if (programUsesWdt(program)) {
|
|
901
|
+
includes.push("<avr/wdt.h>");
|
|
902
|
+
}
|
|
748
903
|
// Placeholder defaults for fields that are computed later by other phases
|
|
749
904
|
const noopFixPointer = (c) => c;
|
|
750
905
|
// Demo #18 Finding B: collect the names of non-exported free functions that
|
|
@@ -597,6 +597,8 @@ export function runTopLevelPreprocessing(ctx) {
|
|
|
597
597
|
statements: callbackIR.statements ?? callbackIR.body ?? [],
|
|
598
598
|
debounceMs: callbackIR.debounceMs,
|
|
599
599
|
isInterruptHandler: callbackIR.isInterruptHandler,
|
|
600
|
+
...(callbackIR.returnType ? { returnType: callbackIR.returnType } : {}),
|
|
601
|
+
...(callbackIR.typedParams ? { typedParams: callbackIR.typedParams } : {}),
|
|
600
602
|
});
|
|
601
603
|
replacePlaceholderInAllStatements(filteredTopLevelExecutables, rc.placeholderName, callbackName);
|
|
602
604
|
for (const fn of mappedFunctions) {
|
|
@@ -34,7 +34,7 @@ import { getRadioGroups } from "../../ir/ui-element-auto-wire.js";
|
|
|
34
34
|
// minPressure gate is skipped for them. Resistive controllers (XPT2046,
|
|
35
35
|
// STMPE610, Adafruit_TouchScreen) keep the gate.
|
|
36
36
|
// ---------------------------------------------------------------------------
|
|
37
|
-
const CAPACITIVE_TOUCH_LIBS = new Set(["FT6336U", "sdl"]);
|
|
37
|
+
const CAPACITIVE_TOUCH_LIBS = new Set(["FT6336U", "GT911", "CST816S", "sdl"]);
|
|
38
38
|
/** Generate the ui_poll_touch() body, or null for the no-touch path. */
|
|
39
39
|
export function generateTouchPollBody(input) {
|
|
40
40
|
const { library, minPressure, calibration, profile } = input;
|
|
@@ -141,9 +141,14 @@ export function emitUIRuntime(ctx) {
|
|
|
141
141
|
// display-adapter registry. Must precede the runtime header so the
|
|
142
142
|
// adapter functions (__tc_display, display_init, etc.) are available.
|
|
143
143
|
const profile = getDisplayProfile();
|
|
144
|
-
// 0. SPI-bus display drivers (ILI9341, ST7796, …) need the SPI
|
|
145
|
-
//
|
|
146
|
-
|
|
144
|
+
// 0. SPI-bus display drivers (ILI9341, ST7796, …) need the Arduino SPI
|
|
145
|
+
// library — but ONLY on the Adafruit path. Native adapters (AVR, ESP32)
|
|
146
|
+
// emit their own bus setup and would break on <SPI.h>. Host render targets
|
|
147
|
+
// (sdl, native-preview) have no SPI bus at all.
|
|
148
|
+
const nativeAdapter = ctx.strategy?.providesDisplayAdapter?.() ?? false;
|
|
149
|
+
const spiDriver = profile?.driver !== "sdl"
|
|
150
|
+
&& profile?.driver !== "native-preview"
|
|
151
|
+
&& !nativeAdapter;
|
|
147
152
|
if (spiDriver && !ctx.includes.includes("<SPI.h>")) {
|
|
148
153
|
ctx.includes.push("<SPI.h>");
|
|
149
154
|
}
|
|
@@ -151,7 +156,7 @@ export function emitUIRuntime(ctx) {
|
|
|
151
156
|
// buildEmitterContext (setup.ts), NOT here — emitPreamble runs before this
|
|
152
157
|
// function, so a push here would be too late to land in the output.
|
|
153
158
|
if (profile) {
|
|
154
|
-
const adapter = generateDisplayAdapter(profile);
|
|
159
|
+
const adapter = generateDisplayAdapter(profile, ctx.strategy);
|
|
155
160
|
// Prepend includes to the very front — Arduino's auto-prototyper scans
|
|
156
161
|
// the whole .ino and generates prototypes that reference GFXcanvas16
|
|
157
162
|
// etc. before any #include, so the GFX header must come first.
|
|
@@ -164,6 +169,14 @@ export function emitUIRuntime(ctx) {
|
|
|
164
169
|
ctx.sourceLines.push(is888
|
|
165
170
|
? "#define UI_COLOR_DEPTH 888\n#define UI_COLOR_T uint32_t\n#define UI_DIM_MASK 0x7F7F7Fu"
|
|
166
171
|
: "#define UI_COLOR_DEPTH 565\n#define UI_COLOR_T uint16_t\n#define UI_DIM_MASK 0x7BEFu");
|
|
172
|
+
// Native display adapters (AVR, ESP32) instantiate CuttlefishGFX by value
|
|
173
|
+
// in their declaration block. The class MUST be defined before that —
|
|
174
|
+
// emit the cuttlefish-gfx slice here, between the color preamble and the
|
|
175
|
+
// adapter declaration. Arduino path (Adafruit) returns "" from the slice.
|
|
176
|
+
const nativeDisplayActive = ctx.strategy?.providesDisplayAdapter?.() ?? false;
|
|
177
|
+
if (nativeDisplayActive) {
|
|
178
|
+
ctx.sourceLines.push(ui.emitCuttlefishGfx(true));
|
|
179
|
+
}
|
|
167
180
|
ctx.sourceLines.push(adapter.declaration);
|
|
168
181
|
ctx.sourceLines.push(adapter.functions);
|
|
169
182
|
}
|
|
@@ -173,7 +186,7 @@ export function emitUIRuntime(ctx) {
|
|
|
173
186
|
if (profile.touch) {
|
|
174
187
|
const t = profile.touch;
|
|
175
188
|
if (t.library) {
|
|
176
|
-
touchAdapter = generateTouchAdapter(t);
|
|
189
|
+
touchAdapter = generateTouchAdapter(t, ctx.strategy);
|
|
177
190
|
// Prepend includes to the front (Arduino auto-prototyper safety).
|
|
178
191
|
ctx.sourceLines.unshift(touchAdapter.includes[0]);
|
|
179
192
|
for (let i = 1; i < touchAdapter.includes.length; i++) {
|
|
@@ -252,13 +265,15 @@ export function emitUIRuntime(ctx) {
|
|
|
252
265
|
// header so its #ifndef guards adopt them. Source of truth:
|
|
253
266
|
// resolveScrollConfig(profile.scroll). Defaults derive from the declared
|
|
254
267
|
// touch hardware, so the demo (XPT2046) gets resistive+full with no config.
|
|
255
|
-
const scroll = resolveScrollConfig(profile, { buildTarget: profile._buildTarget });
|
|
268
|
+
const scroll = resolveScrollConfig(profile, { buildTarget: profile._buildTarget, psram: profile._psram });
|
|
256
269
|
ctx.sourceLines.push(`#define UI_SCROLL_MAX_OVERSCROLL ${scroll.maxOverscroll}`, `#define UI_SCROLL_STIFFNESS_X10 ${Math.round(scroll.stiffness * 10)}`, `#define UI_SCROLL_EDGE_SNAP_PX ${scroll.edgeSnapPx}`, `#define UI_SCROLL_DRAG_SCALE_X10 ${Math.round(scroll.dragScale * 10)}`, `#define UI_SCROLL_INPUT_TIER_CAPACITIVE ${scroll.inputTier === "capacitive" ? 1 : 0}`, `#define UI_SCROLL_INPUT_TIER_RESISTIVE ${scroll.inputTier === "resistive" ? 1 : 0}`, `#define UI_SCROLL_INPUT_TIER_NONE ${scroll.inputTier === "none" ? 1 : 0}`, `#define UI_SCROLL_RENDER_TIER_FULL ${scroll.renderTier === "full" ? 1 : 0}`, `#define UI_SCROLL_RENDER_TIER_CONSTRAINED ${scroll.renderTier === "constrained" ? 1 : 0}`, `#define UI_SCROLL_CANVAS_BUDGET_BYTES ${scroll.scrollCanvasBudgetBytes}`, ...(scroll.debug ? [`#define UI_SCROLL_DEBUG 1`] : []));
|
|
257
270
|
// Forward-declare __ui_kb_set_onchange before the runtime header: the
|
|
258
271
|
// header's keyboard-open function calls it, but the definition is emitted
|
|
259
272
|
// later (section 8b). On native (single TU) this must precede the header.
|
|
260
273
|
ctx.sourceLines.push(`void __ui_kb_set_onchange();`);
|
|
261
|
-
ctx.sourceLines.push(emitRuntimeHeader(
|
|
274
|
+
ctx.sourceLines.push(emitRuntimeHeader({
|
|
275
|
+
nativeDisplayActive: ctx.strategy?.providesDisplayAdapter?.() ?? false,
|
|
276
|
+
}));
|
|
262
277
|
// 1.5. Touch poll function (uses the adapter pattern). Body is generated by
|
|
263
278
|
// generateTouchPollBody — extracted for unit-testability of the capacitive
|
|
264
279
|
// vs resistive branch (FT6336U/SDL skip the minPressure gate).
|
|
@@ -10,6 +10,7 @@ import { accessorGetterName } from "./utils/cpp-helpers.js";
|
|
|
10
10
|
import { INTEGRAL_CPP_TYPE_RE } from "./utils/cpp-helpers.js";
|
|
11
11
|
import { renderPeripheralProperty } from "../mapping/peripheral-names.js";
|
|
12
12
|
import { parseCppType, renderCppType, bareType, parsedIsPointer, parsedIsVector, needsCStrForStringLike } from "../api/shared/cpp-type-ir.js";
|
|
13
|
+
import { cppTypeForHalOp } from "./utils/hal-op-cpp-type.js";
|
|
13
14
|
/**
|
|
14
15
|
* Renders ExpressionIR nodes to C++ code strings.
|
|
15
16
|
*/
|
|
@@ -403,6 +404,8 @@ export class ExpressionRenderer {
|
|
|
403
404
|
}
|
|
404
405
|
case "unary":
|
|
405
406
|
return expr.operator === "!" ? "bool" : this.inferExpressionCppType(expr.operand, knownVariableTypes);
|
|
407
|
+
case "hal-expr":
|
|
408
|
+
return cppTypeForHalOp(expr.operation.operation);
|
|
406
409
|
default:
|
|
407
410
|
return undefined;
|
|
408
411
|
}
|
|
@@ -714,10 +717,16 @@ export class ExpressionRenderer {
|
|
|
714
717
|
}
|
|
715
718
|
case "hal-expr": {
|
|
716
719
|
const rendered = this.render(expr, exprTransformer);
|
|
717
|
-
// HAL expressions that resolve to string-like outputs use %s
|
|
718
720
|
if (rendered.startsWith('"') || this.stringVarNames?.has(rendered)) {
|
|
719
721
|
return { format: "%s", arg: rendered, estimatedLength: 32 };
|
|
720
722
|
}
|
|
723
|
+
const cppType = cppTypeForHalOp(expr.operation.operation);
|
|
724
|
+
if (cppType && this.isStringLikeCppType(cppType)) {
|
|
725
|
+
return { format: "%s", arg: rendered, estimatedLength: 32 };
|
|
726
|
+
}
|
|
727
|
+
if (cppType === "bool") {
|
|
728
|
+
return { format: "%s", arg: `(${rendered} ? "true" : "false")`, estimatedLength: 5 };
|
|
729
|
+
}
|
|
721
730
|
return { format: "%d", arg: rendered, estimatedLength: 12 };
|
|
722
731
|
}
|
|
723
732
|
case "method-call":
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// Types are imported from @typecad/cuttlefish/api/shared.
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
10
10
|
import { escapeCppStringLiteral, escapeSnprintfFormatFragment } from "../utils/strings.js";
|
|
11
|
+
import { cppTypeForHalOp } from "./utils/hal-op-cpp-type.js";
|
|
11
12
|
import { parsedElementString, parsedIsStringLike } from "../api/shared/cpp-type-ir.js";
|
|
12
13
|
// ---------------------------------------------------------------------------
|
|
13
14
|
// Scope state management
|
|
@@ -369,6 +370,13 @@ export function inferSnprintfArg(expr, strategy, scopeState, renderExpression, p
|
|
|
369
370
|
if (rendered.startsWith('"') || stringVarNames?.has(rendered)) {
|
|
370
371
|
return { format: "%s", arg: rendered, estimatedLength: 32, preludeLines: [] };
|
|
371
372
|
}
|
|
373
|
+
const cppType = cppTypeForHalOp(expr.operation.operation);
|
|
374
|
+
if (cppType && strategy.isStringLikeType(cppType)) {
|
|
375
|
+
return { format: "%s", arg: rendered, estimatedLength: 32, preludeLines: [] };
|
|
376
|
+
}
|
|
377
|
+
if (cppType === "bool") {
|
|
378
|
+
return { format: "%s", arg: `(${rendered} ? "true" : "false")`, estimatedLength: 5, preludeLines: [] };
|
|
379
|
+
}
|
|
372
380
|
return { format: "%d", arg: rendered, estimatedLength: 12, preludeLines: [] };
|
|
373
381
|
}
|
|
374
382
|
default:
|
|
@@ -485,6 +485,19 @@ export class StatementRenderer {
|
|
|
485
485
|
const rawStmt = statement.callee.slice('__RAW_STMT__'.length);
|
|
486
486
|
return forHeader ? rawStmt : `${rawStmt.endsWith(';') ? rawStmt : rawStmt + ';'}`;
|
|
487
487
|
}
|
|
488
|
+
// Awaited network markers (__WIFI_WAIT__/__HTTP_WAIT__) reaching the
|
|
489
|
+
// plain renderer means the await sits outside an async state machine
|
|
490
|
+
// (top-level await, or a position the state-machine splitter doesn't
|
|
491
|
+
// support). Fall back to the blocking form of the op they carry.
|
|
492
|
+
if ((statement.callee === "__WIFI_WAIT__" || statement.callee === "__HTTP_WAIT__" || statement.callee === "__BLE_WAIT__" || statement.callee === "__HAL_WAIT__")
|
|
493
|
+
&& statement.args[0]?.kind === "hal-expr") {
|
|
494
|
+
const resolved = routeHALOp(statement.args[0].operation, this.strategy);
|
|
495
|
+
const code = resolved?.code ?? (resolved?.expression ? `${resolved.expression};` : undefined);
|
|
496
|
+
if (code) {
|
|
497
|
+
return forHeader ? code.replace(/;$/, "") : code;
|
|
498
|
+
}
|
|
499
|
+
return `/* unhandled awaited hal-op: ${statement.args[0].operation.operation} */`;
|
|
500
|
+
}
|
|
488
501
|
// Handle emit() — compile-time C++ injection
|
|
489
502
|
if (statement.callee === "__EMIT__") {
|
|
490
503
|
const rawText = statement.args.map(arg => this.renderEmitArg(arg)).join("");
|