@typecad/cuttlefish 1.0.0-alpha.14 → 1.0.0-alpha.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -79
- package/assets/editor-extensions/typecad-debug/README.md +25 -26
- package/dist/api/config.d.ts +22 -26
- package/dist/api/schema/types.d.ts +20 -3
- package/dist/api/shared/async-runtime-static.d.ts +1 -1
- package/dist/api/shared/async-runtime-static.js +5 -5
- package/dist/api/shared/async-types.d.ts +3 -2
- package/dist/api/shared/board-catalog.d.ts +2 -0
- package/dist/api/shared/board-catalog.js +6 -0
- package/dist/api/shared/board-resolver.d.ts +4 -7
- package/dist/api/shared/board-resolver.js +2 -1
- package/dist/api/shared/coop-scheduler.d.ts +1 -1
- package/dist/api/shared/coop-scheduler.js +1 -1
- package/dist/api/shared/cpp-type-ir.d.ts +1 -1
- package/dist/api/shared/cpp-type-ir.js +1 -1
- package/dist/api/shared/display-profile.d.ts +1 -1
- package/dist/api/shared/display-profile.js +3 -5
- package/dist/api/shared/framework-manifest-registry.d.ts +1 -1
- package/dist/api/shared/framework-manifest-registry.js +0 -1
- package/dist/api/shared/framework-manifest.d.ts +7 -6
- package/dist/api/shared/framework-manifest.js +6 -11
- package/dist/api/shared/hal-op-ir.d.ts +294 -685
- package/dist/api/shared/hal-op-ir.js +31 -65
- package/dist/api/shared/index.d.ts +0 -4
- package/dist/api/shared/index.js +1 -7
- package/dist/api/shared/ir-core.d.ts +6 -0
- package/dist/api/shared/ir-declarations.d.ts +4 -0
- package/dist/api/shared/ir.d.ts +1 -3
- package/dist/api/shared/platform-strategy.d.ts +14 -37
- package/dist/api/shared/polyfill-helper-registry.js +0 -5
- package/dist/api/shared/polyfill-types.js +0 -27
- package/dist/api/shared/promise-runtime.js +3 -3
- package/dist/api/shared/spdx-licenses.d.ts +12 -10
- package/dist/api/shared/spdx-licenses.js +1 -1
- package/dist/api/shared/toolchain-types.d.ts +1 -1
- package/dist/api/shared/toolchain-types.js +3 -3
- package/dist/api/shared/types.d.ts +0 -3
- package/dist/api/shared/validate-framework-manifest.js +13 -33
- package/dist/board-catalog/dts-reader.d.ts +220 -0
- package/dist/board-catalog/dts-reader.js +1052 -0
- package/dist/board-catalog/index.d.ts +11 -0
- package/dist/board-catalog/index.js +12 -0
- package/dist/board-catalog/pinconfig.d.ts +48 -0
- package/dist/board-catalog/pinconfig.js +263 -0
- package/dist/board-catalog/sdk.d.ts +82 -0
- package/dist/board-catalog/sdk.js +180 -0
- package/dist/board-catalog/store.d.ts +196 -0
- package/dist/board-catalog/store.js +363 -0
- package/dist/board-catalog/types.d.ts +233 -0
- package/dist/board-catalog/types.js +7 -0
- package/dist/board-catalog/walker.d.ts +112 -0
- package/dist/board-catalog/walker.js +1101 -0
- package/dist/cli-utils.d.ts +2 -2
- package/dist/cli-utils.js +5 -5
- package/dist/cli.js +214 -242
- package/dist/config-loader.d.ts +14 -19
- package/dist/config-loader.js +240 -78
- package/dist/config-schema.d.ts +3 -5
- package/dist/config-schema.js +8 -8
- package/dist/contract/board-generator.d.ts +4 -4
- package/dist/contract/board-generator.js +16 -5
- package/dist/contract/contract-parser.d.ts +27 -0
- package/dist/contract/contract-parser.js +76 -0
- package/dist/contract/index.d.ts +1 -1
- package/dist/contract/index.js +70 -21
- package/dist/create/active-board-catalog.d.ts +9 -0
- package/dist/create/active-board-catalog.js +94 -0
- package/dist/create/board-catalog.generated.d.ts +96 -0
- package/dist/create/board-catalog.generated.js +128737 -0
- package/dist/create/board-search.d.ts +23 -0
- package/dist/create/board-search.js +88 -0
- package/dist/create/editor-integration.d.ts +4 -2
- package/dist/create/editor-integration.js +41 -2
- package/dist/create/framework-catalog.d.ts +41 -9
- package/dist/create/framework-catalog.js +48 -57
- package/dist/create/index.d.ts +5 -11
- package/dist/create/index.js +3 -8
- package/dist/create/mcu-target.d.ts +39 -0
- package/dist/create/mcu-target.js +80 -0
- package/dist/create/pack-targets.d.ts +19 -0
- package/dist/create/pack-targets.js +56 -0
- package/dist/create/scaffold.d.ts +4 -7
- package/dist/create/scaffold.js +19 -109
- package/dist/create/templates.d.ts +27 -5
- package/dist/create/templates.js +120 -116
- package/dist/create/wizard.d.ts +12 -1
- package/dist/create/wizard.js +163 -38
- package/dist/debug/preprocessor.d.ts +5 -0
- package/dist/debug/preprocessor.js +31 -7
- package/dist/diagnostics/diagnostics-report.d.ts +1 -1
- package/dist/diagnostics/diagnostics-report.js +3 -11
- package/dist/diagnostics/json-schema.d.ts +1 -1
- package/dist/diagnostics/md-writer.js +2 -2
- package/dist/diagnostics/mermaid-builder.d.ts +1 -1
- package/dist/diagnostics/mermaid-builder.js +1 -1
- package/dist/emit/cpp-emitter.js +4 -1
- package/dist/emit/emitters/class-emitter.js +32 -4
- package/dist/emit/emitters/emitter-context.d.ts +11 -0
- package/dist/emit/emitters/entrypoint-synthesizer.js +1 -1
- package/dist/emit/emitters/function-emitter-impl.js +33 -17
- package/dist/emit/emitters/output-finalizer.d.ts +7 -0
- package/dist/emit/emitters/output-finalizer.js +31 -23
- package/dist/emit/emitters/setup.js +125 -63
- package/dist/emit/emitters/top-level-prep.js +1 -1
- package/dist/emit/emitters/type-decl-emitter.js +13 -8
- package/dist/emit/emitters/ui-emitter.js +1 -1
- package/dist/emit/expression-renderer.d.ts +3 -3
- package/dist/emit/expression-renderer.js +17 -7
- package/dist/emit/route-hal-op.js +17 -48
- package/dist/emit/snprintf-helpers.d.ts +1 -3
- package/dist/emit/snprintf-helpers.js +1 -39
- package/dist/emit/statement-renderer.d.ts +0 -4
- package/dist/emit/statement-renderer.js +54 -45
- package/dist/emit/utils/async-state-machine.d.ts +16 -9
- package/dist/emit/utils/async-state-machine.js +56 -54
- package/dist/emit/utils/comment-helpers.d.ts +0 -8
- package/dist/emit/utils/comment-helpers.js +1 -1
- package/dist/emit/utils/cpp-helpers.d.ts +2 -2
- package/dist/emit/utils/cpp-helpers.js +2 -2
- package/dist/emit/utils/hal-op-cpp-type.js +4 -6
- package/dist/emit/utils/index.d.ts +3 -3
- package/dist/emit/utils/index.js +3 -3
- package/dist/emit/utils/type-inference.d.ts +1 -29
- package/dist/emit/utils/type-inference.js +0 -75
- package/dist/framework-package.d.ts +3 -0
- package/dist/framework-package.js +23 -6
- package/dist/framework-registry.d.ts +5 -5
- package/dist/frameworks/native/graphics/terminal-preview.d.ts +5 -0
- package/dist/frameworks/native/graphics/terminal-preview.js +33 -0
- package/dist/frameworks/native/index.d.ts +3 -0
- package/dist/frameworks/native/index.js +15 -0
- package/dist/frameworks/native/native-compile.d.ts +17 -0
- package/dist/frameworks/native/native-compile.js +170 -0
- package/dist/frameworks/native/native-config.d.ts +41 -0
- package/dist/frameworks/native/native-config.js +7 -0
- package/dist/frameworks/native/strategy.d.ts +97 -0
- package/dist/frameworks/native/strategy.js +664 -0
- package/dist/ir/board-resolver.d.ts +18 -23
- package/dist/ir/board-resolver.js +43 -401
- package/dist/ir/build-ir-state.d.ts +3 -0
- package/dist/ir/build-ir-state.js +42 -0
- package/dist/ir/build-ir.d.ts +1 -1
- package/dist/ir/build-ir.js +40 -42
- package/dist/ir/declaration-builders.js +1 -0
- package/dist/ir/expression-to-ir.js +17 -10
- package/dist/ir/hal/hal-emitter.d.ts +0 -2
- package/dist/ir/hal/hal-emitter.js +92 -41
- package/dist/ir/hal/hal-parser.d.ts +5 -5
- package/dist/ir/hal/hal-parser.js +128 -36
- package/dist/ir/hal/hal-plugins.d.ts +0 -1
- package/dist/ir/hal/hal-plugins.js +425 -716
- package/dist/ir/heap-analysis.js +2 -41
- package/dist/ir/identifier-collector.js +32 -20
- package/dist/ir/network-validation.js +7 -11
- package/dist/ir/ownership-analysis.js +75 -13
- package/dist/ir/peripheral-usage.d.ts +8 -2
- package/dist/ir/peripheral-usage.js +78 -149
- package/dist/ir/peripheral-validation.js +23 -3
- package/dist/ir/pin-capability-validation.d.ts +12 -0
- package/dist/ir/pin-capability-validation.js +61 -97
- package/dist/ir/pin-mode-validation.js +5 -5
- package/dist/ir/program-analysis.d.ts +8 -15
- package/dist/ir/program-analysis.js +140 -323
- package/dist/ir/pulldown-validation.js +11 -4
- package/dist/ir/resource-analysis.js +15 -3
- package/dist/ir/statement-to-ir.js +0 -6
- package/dist/ir/timing-validation.js +1 -1
- package/dist/ir/transformers/array-methods.js +11 -5
- package/dist/ir/transformers/call-statement.js +20 -87
- package/dist/ir/transformers/callback-context-registry.d.ts +1 -1
- package/dist/ir/transformers/callback-context-registry.js +8 -16
- package/dist/ir/transformers/control-flow.js +0 -13
- package/dist/ir/transformers/expressions.js +10 -9
- package/dist/ir/transformers/hal-call-resolver.d.ts +2 -2
- package/dist/ir/transformers/hal-call-resolver.js +24 -10
- package/dist/ir/transformers/hal-emit-helpers.d.ts +2 -2
- package/dist/ir/transformers/hal-emit-helpers.js +2 -2
- package/dist/ir/transformers/namespace-methods.js +1 -1
- package/dist/ir/transformers/ui-callback-lowering.d.ts +0 -2
- package/dist/ir/transformers/ui-callback-lowering.js +0 -24
- package/dist/ir/transformers/variables.js +338 -42
- package/dist/ir/try-catch-validation.js +4 -3
- package/dist/ir/type-resolution.js +15 -4
- package/dist/ir/validation-orchestrator.js +0 -9
- package/dist/libdef/cpp-to-decl.d.ts +0 -9
- package/dist/libdef/cpp-to-decl.js +0 -72
- package/dist/libdef/registry.d.ts +0 -1
- package/dist/libdef/registry.js +1 -24
- package/dist/library/init.d.ts +1 -1
- package/dist/library/init.js +137 -137
- package/dist/library-packages.d.ts +1 -1
- package/dist/library-packages.js +1 -1
- package/dist/mapping/peripheral-names.js +9 -4
- package/dist/mapping/source-map.d.ts +3 -4
- package/dist/mapping/source-map.js +12 -18
- package/dist/orchestrator/graph-builder.d.ts +3 -3
- package/dist/orchestrator/graph-builder.js +13 -12
- package/dist/orchestrator/type-checker.d.ts +3 -3
- package/dist/orchestrator/type-checker.js +3 -3
- package/dist/platform/async-runtime.js +1 -1
- package/dist/platform/coop-scheduler-runtime.js +2 -2
- package/dist/platform/generic-debug-codegen.d.ts +36 -0
- package/dist/platform/generic-debug-codegen.js +185 -0
- package/dist/platform/generic-strategy.d.ts +3 -4
- package/dist/platform/generic-strategy.js +5 -80
- package/dist/platform/index.js +1 -1
- package/dist/platform/registry.d.ts +2 -2
- package/dist/platform/registry.js +2 -2
- package/dist/safety/asil-decorators.d.ts +32 -0
- package/dist/safety/asil-decorators.js +32 -0
- package/dist/safety/authoring.d.ts +85 -0
- package/dist/safety/authoring.js +61 -0
- package/dist/safety/engine.d.ts +3 -0
- package/dist/safety/engine.js +83 -0
- package/dist/safety/hal/ops.d.ts +48 -0
- package/dist/safety/hal/ops.js +46 -0
- package/dist/safety/iso26262/analyze.d.ts +13 -0
- package/dist/safety/iso26262/analyze.js +29 -0
- package/dist/safety/iso26262/asil.d.ts +10 -0
- package/dist/safety/iso26262/asil.js +22 -0
- package/dist/safety/iso26262/collect.d.ts +14 -0
- package/dist/safety/iso26262/collect.js +137 -0
- package/dist/safety/iso26262/goto-checker.d.ts +10 -0
- package/dist/safety/iso26262/goto-checker.js +72 -0
- package/dist/safety/iso26262/heap-checker.d.ts +7 -0
- package/dist/safety/iso26262/heap-checker.js +57 -0
- package/dist/safety/iso26262/init-checker.d.ts +13 -0
- package/dist/safety/iso26262/init-checker.js +127 -0
- package/dist/safety/iso26262/loop-checker.d.ts +7 -0
- package/dist/safety/iso26262/loop-checker.js +86 -0
- package/dist/safety/iso26262/recursion-checker.d.ts +9 -0
- package/dist/safety/iso26262/recursion-checker.js +134 -0
- package/dist/safety/iso26262/sidecar-writer.d.ts +25 -0
- package/dist/safety/iso26262/sidecar-writer.js +13 -0
- package/dist/safety/passes/pinMode-intercept.d.ts +3 -0
- package/dist/safety/passes/pinMode-intercept.js +60 -0
- package/dist/safety/runtime/mode-table.d.ts +5 -0
- package/dist/safety/runtime/mode-table.js +55 -0
- package/dist/safety/runtime/polyfills.d.ts +6 -0
- package/dist/safety/runtime/polyfills.js +12 -0
- package/dist/safety/runtime/result-struct.d.ts +11 -0
- package/dist/safety/runtime/result-struct.js +98 -0
- package/dist/safety/runtime/safe-int.d.ts +33 -0
- package/dist/safety/runtime/safe-int.js +170 -0
- package/dist/safety/runtime/safe-traits.d.ts +4 -0
- package/dist/safety/runtime/safe-traits.js +95 -0
- package/dist/safety/runtime/safe-variable.d.ts +39 -0
- package/dist/safety/runtime/safe-variable.js +226 -0
- package/dist/safety/runtime/vote.d.ts +8 -0
- package/dist/safety/runtime/vote.js +92 -0
- package/dist/safety/runtime/write-verify.d.ts +8 -0
- package/dist/safety/runtime/write-verify.js +71 -0
- package/dist/safety/safe-int-types.d.ts +45 -0
- package/dist/safety/safe-int-types.js +25 -0
- package/dist/safety/safe-variable-types.d.ts +29 -0
- package/dist/safety/safe-variable-types.js +19 -0
- package/dist/safety/safety-bridge.d.ts +5 -6
- package/dist/safety/safety-bridge.js +15 -28
- package/dist/safety/sidecar-bridge.d.ts +1 -3
- package/dist/safety/sidecar-bridge.js +3 -20
- package/dist/safety/specifiers.d.ts +6 -0
- package/dist/safety/specifiers.js +14 -0
- package/dist/safety-hook.d.ts +8 -7
- package/dist/safety-hook.js +12 -13
- package/dist/testing.d.ts +4 -5
- package/dist/testing.js +5 -5
- package/dist/transpile/resolution.d.ts +1 -1
- package/dist/transpile/resolution.js +18 -9
- package/dist/transpile/test-pins.d.ts +18 -0
- package/dist/transpile/test-pins.js +142 -0
- package/dist/transpile.d.ts +1 -2
- package/dist/transpile.js +36 -30
- package/dist/types.d.ts +26 -26
- package/dist/ui-hook.d.ts +1 -1
- package/dist/utils/cli.d.ts +2 -2
- package/dist/utils/cli.js +92 -154
- package/dist/utils/fs.d.ts +3 -4
- package/dist/utils/fs.js +4 -5
- package/dist/utils/strings.d.ts +0 -1
- package/dist/utils/strings.js +0 -8
- package/dist/utils/ui.d.ts +5 -2
- package/dist/utils/ui.js +13 -3
- package/package.json +158 -157
- package/dist/add-preset.d.ts +0 -4
- package/dist/add-preset.js +0 -74
- package/dist/api/shared/async-symbol-detector.d.ts +0 -11
- package/dist/api/shared/async-symbol-detector.js +0 -140
- package/dist/api/shared/native-display-op-resolver.d.ts +0 -10
- package/dist/api/shared/native-display-op-resolver.js +0 -64
- package/dist/create/board-checklist.d.ts +0 -2
- package/dist/create/board-checklist.js +0 -52
- package/dist/create/board-codegen.d.ts +0 -9
- package/dist/create/board-codegen.js +0 -238
- package/dist/create/board-generators.d.ts +0 -12
- package/dist/create/board-generators.js +0 -651
- package/dist/create/board-spec.d.ts +0 -181
- package/dist/create/board-spec.js +0 -214
- package/dist/install/framework-catalog.d.ts +0 -53
- package/dist/install/framework-catalog.js +0 -107
- package/dist/install/handle-install.d.ts +0 -35
- package/dist/install/handle-install.js +0 -177
- package/dist/install/index.d.ts +0 -4
- package/dist/install/index.js +0 -3
- package/dist/ir/pin-state-tracking.d.ts +0 -58
- package/dist/ir/pin-state-tracking.js +0 -182
- package/dist/ir/pwm-timer-sharing.d.ts +0 -4
- package/dist/ir/pwm-timer-sharing.js +0 -94
- package/dist/ir/timer0-pwm-timing-conflict.d.ts +0 -4
- package/dist/ir/timer0-pwm-timing-conflict.js +0 -72
- package/dist/libdef/component-decls.d.ts +0 -2
- package/dist/libdef/component-decls.js +0 -6
- package/dist/libdef/component-discovery.d.ts +0 -43
- package/dist/libdef/component-discovery.js +0 -83
- package/dist/theme-tokens.d.ts +0 -22
- package/dist/theme-tokens.js +0 -172
|
@@ -179,8 +179,17 @@ export class ExpressionRenderer {
|
|
|
179
179
|
break;
|
|
180
180
|
case "hal-expr": {
|
|
181
181
|
const resolved = routeHALOp(expr.operation, this.strategy);
|
|
182
|
+
// A method body's preceding side-effect ops (bus transactions, pin
|
|
183
|
+
// configures) must run even in expression position — wrap them with
|
|
184
|
+
// the value in a GCC statement-expression.
|
|
185
|
+
const prefix = (expr.prefixOps ?? [])
|
|
186
|
+
.map((op) => routeHALOp(op, this.strategy))
|
|
187
|
+
.map((r) => r?.code ?? `/* unhandled hal-op prefix: dropped */`)
|
|
188
|
+
.join(' ');
|
|
182
189
|
if (resolved?.expression) {
|
|
183
|
-
rendered =
|
|
190
|
+
rendered = prefix
|
|
191
|
+
? `({ ${prefix} ${resolved.expression}; })`
|
|
192
|
+
: resolved.expression;
|
|
184
193
|
}
|
|
185
194
|
else if (resolved?.code) {
|
|
186
195
|
// Strip trailing semicolon and a leading `return ` for expression
|
|
@@ -189,7 +198,8 @@ export class ExpressionRenderer {
|
|
|
189
198
|
// `return` keyword is invalid (avr-g++: "expected primary-expression
|
|
190
199
|
// before 'return'") and would leak as
|
|
191
200
|
// `strcmp(return Preferences.getString(...), ...)`. Demo #33 Finding C.
|
|
192
|
-
|
|
201
|
+
const asExpr = resolved.code.replace(/;\s*$/, "").replace(/^\s*return\s+/, "");
|
|
202
|
+
rendered = prefix ? `({ ${prefix} ${asExpr}; })` : asExpr;
|
|
193
203
|
}
|
|
194
204
|
else {
|
|
195
205
|
// Unregistered HAL op: surface as a warning so the user sees it, but
|
|
@@ -862,7 +872,7 @@ export class ExpressionRenderer {
|
|
|
862
872
|
// must be cast to an integral type — a C++ `enum class` does not
|
|
863
873
|
// implicitly convert to `size_t`, so `vector[enumValue]` fails to compile.
|
|
864
874
|
// `renderEnumSafeValue` already wraps numeric-enum operands in
|
|
865
|
-
// `static_cast<int>(...)
|
|
875
|
+
// `static_cast<int>(...)`; route the index through it
|
|
866
876
|
// (with the in-scope variable types so a bare enum-typed identifier
|
|
867
877
|
// resolves) so enum indices lower correctly. Non-enum indices are passed
|
|
868
878
|
// through unchanged. Demo #28 Finding E.
|
|
@@ -886,8 +896,8 @@ export class ExpressionRenderer {
|
|
|
886
896
|
* String-enum operands are passed through unchanged (they're `const char*`).
|
|
887
897
|
*
|
|
888
898
|
* This is the assignment/argument-site counterpart to the operator-level
|
|
889
|
-
* enum wrapping in renderBinary.
|
|
890
|
-
*
|
|
899
|
+
* enum wrapping in renderBinary. Enum values used as integers need an
|
|
900
|
+
* explicit cast in C++.
|
|
891
901
|
*/
|
|
892
902
|
renderEnumSafeValue(expr, knownVariableTypes) {
|
|
893
903
|
const rendered = this.render(expr, undefined, knownVariableTypes);
|
|
@@ -919,7 +929,7 @@ export class ExpressionRenderer {
|
|
|
919
929
|
* operand → `static_cast<EnumType>(value)` (int → enum). C++ `enum
|
|
920
930
|
* class` does not implicitly convert FROM an integral storage type
|
|
921
931
|
* either, so reading an `int`/`uint8_t` cell back into an enum-typed
|
|
922
|
-
* local needs the reverse cast.
|
|
932
|
+
* local needs the reverse cast.
|
|
923
933
|
*
|
|
924
934
|
* This closes the "enum↔integral storage boundary" family — the previous
|
|
925
935
|
* point-specific casts handled enum-as-array-index (demo #28 E) and
|
|
@@ -1155,7 +1165,7 @@ export class ExpressionRenderer {
|
|
|
1155
1165
|
// Wrap enum-class operands in static_cast<int>() for arithmetic operators
|
|
1156
1166
|
// (+, -, *, /) and bitwise operators (&, |, ^, <<, >>). C++ enum class
|
|
1157
1167
|
// values don't interoperate with int implicitly — `trophic - 1`, `a | b`,
|
|
1158
|
-
// `x & MASK` all fail without the cast.
|
|
1168
|
+
// `x & MASK` all fail without the cast. The
|
|
1159
1169
|
// shared castEnumOperandsForOperator helper handles detection + defensive
|
|
1160
1170
|
// symmetry in one place.
|
|
1161
1171
|
const numericOps = new Set(["+", "-", "*", "/", "&", "|", "^", "<<", ">>"]);
|
|
@@ -11,70 +11,39 @@
|
|
|
11
11
|
//
|
|
12
12
|
// Output-pin state tracking is also handled here, centrally, so every
|
|
13
13
|
// strategy benefits without per-strategy changes:
|
|
14
|
-
// - gpio
|
|
15
|
-
// read of a direction-only output is not portable, e.g. Zephyr); it folds
|
|
16
|
-
// to a constant or the tracked shadow variable.
|
|
17
|
-
// - gpio.write / gpio.toggle flagged `updatesShadow` get the shadow variable
|
|
14
|
+
// - (pin-state-tracking removed: every gpio.* op lowers through the strategy)
|
|
18
15
|
// update appended to whatever the strategy produced. The flag is baked
|
|
19
16
|
// into the op at IR-build time (markShadowUpdatingOps) — emit must not
|
|
20
17
|
// consult live tracker state, because all files build (each resetting the
|
|
21
18
|
// tracker) before any file emits.
|
|
22
19
|
// ---------------------------------------------------------------------------
|
|
23
20
|
import { getSafetyHook } from "../safety-hook.js";
|
|
24
|
-
import {
|
|
21
|
+
import { markHalOpResolved } from "../ir/build-ir-state.js";
|
|
25
22
|
export function routeHALOp(op, strategy) {
|
|
23
|
+
// An op resolved here during IR-build-time method inlining may never exist
|
|
24
|
+
// as an IR node (its text is baked into the calling method's emit lines),
|
|
25
|
+
// so record it for program-analysis's peripheral usage flags — every
|
|
26
|
+
// branch, matching the IR-node paths (display.* sets usesGPIO/usesDisplay,
|
|
27
|
+
// safety.* maps the __tc_safety_* helpers).
|
|
26
28
|
if (typeof op.operation === "string") {
|
|
27
29
|
if (op.operation.startsWith("display.")) {
|
|
28
|
-
|
|
30
|
+
const r = strategy.resolveDisplayOp?.(op);
|
|
31
|
+
if (r)
|
|
32
|
+
markHalOpResolved(op.operation);
|
|
33
|
+
return r;
|
|
29
34
|
}
|
|
30
35
|
if (op.operation.startsWith("safety.")) {
|
|
31
36
|
// Safety ops are resolved by the @typecad/safety package via hook.
|
|
32
37
|
// Returns undefined if the package is not loaded (caller emits an
|
|
33
38
|
// unhandled-op warning).
|
|
34
|
-
|
|
39
|
+
const r = getSafetyHook()?.resolveSafetyOp?.(op);
|
|
40
|
+
if (r)
|
|
41
|
+
markHalOpResolved(op.operation);
|
|
42
|
+
return r;
|
|
35
43
|
}
|
|
36
44
|
}
|
|
37
|
-
if (op.operation === "gpio.read" && op.trackedValue) {
|
|
38
|
-
// Tracked OUTPUT-pin read: software truth, never a hardware read.
|
|
39
|
-
if (op.trackedValue === "high")
|
|
40
|
-
return { expression: "true" };
|
|
41
|
-
if (op.trackedValue === "low")
|
|
42
|
-
return { expression: "false" };
|
|
43
|
-
return { expression: pinShadowVarName(op.pin) };
|
|
44
|
-
}
|
|
45
45
|
const resolved = strategy.resolveHALOperation?.(op);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
// Arduino) — reading the pin back is exactly what tracking avoids. Lower
|
|
49
|
-
// it as a write of the shadow's current value, then flip the shadow.
|
|
50
|
-
if (op.operation === "gpio.toggle" && op.updatesShadow) {
|
|
51
|
-
const varName = pinShadowVarName(op.pin);
|
|
52
|
-
const writeOp = {
|
|
53
|
-
operation: "gpio.write",
|
|
54
|
-
pin: op.pin,
|
|
55
|
-
value: varName,
|
|
56
|
-
...(op.port !== undefined ? { port: op.port } : {}),
|
|
57
|
-
};
|
|
58
|
-
const writeResolved = strategy.resolveHALOperation?.(writeOp);
|
|
59
|
-
const flip = `${varName} = (!${varName});`;
|
|
60
|
-
if (writeResolved?.code) {
|
|
61
|
-
return { ...writeResolved, code: `${writeResolved.code}\n${flip}` };
|
|
62
|
-
}
|
|
63
|
-
if (writeResolved?.expression) {
|
|
64
|
-
return { ...writeResolved, expression: `${writeResolved.expression}, ${flip}` };
|
|
65
|
-
}
|
|
66
|
-
return { code: flip };
|
|
67
|
-
}
|
|
68
|
-
if (op.operation === "gpio.write" && op.updatesShadow) {
|
|
69
|
-
const varName = pinShadowVarName(op.pin);
|
|
70
|
-
const update = `${varName} = ((${op.value}) != 0);`;
|
|
71
|
-
if (resolved?.code) {
|
|
72
|
-
return { ...resolved, code: `${resolved.code}\n${update}` };
|
|
73
|
-
}
|
|
74
|
-
if (resolved?.expression) {
|
|
75
|
-
return { ...resolved, expression: `${resolved.expression}, ${update}` };
|
|
76
|
-
}
|
|
77
|
-
return { code: update };
|
|
78
|
-
}
|
|
46
|
+
if (resolved)
|
|
47
|
+
markHalOpResolved(op.operation);
|
|
79
48
|
return resolved;
|
|
80
49
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ExpressionIR, StatementIR, VariableDeclarationIR } from "../api/shared/index.js";
|
|
2
2
|
import type { PlatformStrategy } from "../api/shared/index.js";
|
|
3
3
|
import type { KnownVariableInfo, SnprintfArgRenderResult, SnprintfRenderResult, EmissionScopeState, SnprintfExpressionRenderer } from "../api/shared/index.js";
|
|
4
4
|
import { escapeCppStringLiteral } from "../utils/strings.js";
|
|
@@ -12,6 +12,4 @@ export declare function createChildEmissionScope(baseScope: EmissionScopeState,
|
|
|
12
12
|
export declare function recordVariableType(statement: VariableDeclarationIR, scopeState: EmissionScopeState): void;
|
|
13
13
|
export { escapeCppStringLiteral };
|
|
14
14
|
export declare function inferSnprintfArg(expr: ExpressionIR, strategy: PlatformStrategy, scopeState: EmissionScopeState, renderExpression: SnprintfExpressionRenderer, pointerVarTypes?: Map<string, string>, knownFunctionReturnTypes?: Map<string, string>, stringVarNames?: Set<string>): SnprintfArgRenderResult | undefined;
|
|
15
|
-
export declare function buildSnprintfRenderResult(expr: ExpressionIR, strategy: PlatformStrategy, scopeState: EmissionScopeState, renderExpression: SnprintfExpressionRenderer, pointerVarTypes?: Map<string, string>, knownFunctionReturnTypes?: Map<string, string>, stringVarNames?: Set<string>): SnprintfRenderResult | undefined;
|
|
16
|
-
export declare function shouldUseSnprintfForString(statement: VariableDeclarationIR | AssignmentIR, strategy: PlatformStrategy): boolean;
|
|
17
15
|
export declare function statementNeedsSnprintf(statement: StatementIR, strategy: PlatformStrategy): boolean;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// All logic lives in the CLI; no framework package dependency.
|
|
8
8
|
// Types are imported from @typecad/cuttlefish/api/shared.
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
10
|
-
import { escapeCppStringLiteral
|
|
10
|
+
import { escapeCppStringLiteral } from "../utils/strings.js";
|
|
11
11
|
import { cppTypeForHalOp } from "./utils/hal-op-cpp-type.js";
|
|
12
12
|
import { parsedElementString, parsedIsStringLike } from "../api/shared/cpp-type-ir.js";
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
@@ -387,46 +387,8 @@ export function inferSnprintfArg(expr, strategy, scopeState, renderExpression, p
|
|
|
387
387
|
}
|
|
388
388
|
}
|
|
389
389
|
// ---------------------------------------------------------------------------
|
|
390
|
-
// Snprintf render result builder
|
|
391
|
-
// ---------------------------------------------------------------------------
|
|
392
|
-
export function buildSnprintfRenderResult(expr, strategy, scopeState, renderExpression, pointerVarTypes, knownFunctionReturnTypes, stringVarNames) {
|
|
393
|
-
if (expr.kind !== "string_concat") {
|
|
394
|
-
return undefined;
|
|
395
|
-
}
|
|
396
|
-
let formatString = "";
|
|
397
|
-
const args = [];
|
|
398
|
-
let estimatedLength = 1;
|
|
399
|
-
const preludeLines = [];
|
|
400
|
-
for (const part of expr.parts) {
|
|
401
|
-
if (part.kind === "string") {
|
|
402
|
-
// These literal parts are concatenated into the snprintf *format* string,
|
|
403
|
-
// so any embedded `%` must be doubled or snprintf misreads it on hardware.
|
|
404
|
-
formatString += escapeSnprintfFormatFragment(part.value);
|
|
405
|
-
estimatedLength += part.value.length;
|
|
406
|
-
continue;
|
|
407
|
-
}
|
|
408
|
-
const innerExpr = part.kind === "template_string" ? part.expression : part;
|
|
409
|
-
const arg = inferSnprintfArg(innerExpr, strategy, scopeState, renderExpression, pointerVarTypes, knownFunctionReturnTypes, stringVarNames);
|
|
410
|
-
if (!arg) {
|
|
411
|
-
return undefined;
|
|
412
|
-
}
|
|
413
|
-
formatString += arg.format;
|
|
414
|
-
args.push(arg.arg);
|
|
415
|
-
estimatedLength += arg.estimatedLength;
|
|
416
|
-
preludeLines.push(...arg.preludeLines);
|
|
417
|
-
}
|
|
418
|
-
return { formatString, args, estimatedLength: Math.max(estimatedLength, 16), preludeLines };
|
|
419
|
-
}
|
|
420
|
-
// ---------------------------------------------------------------------------
|
|
421
390
|
// Snprintf usage detection
|
|
422
391
|
// ---------------------------------------------------------------------------
|
|
423
|
-
export function shouldUseSnprintfForString(statement, strategy) {
|
|
424
|
-
if (!strategy.useSnprintfForStrings()) {
|
|
425
|
-
return false;
|
|
426
|
-
}
|
|
427
|
-
const value = statement.kind === "var_decl" ? statement.initializer : statement.value;
|
|
428
|
-
return value?.kind === "string_concat";
|
|
429
|
-
}
|
|
430
392
|
export function statementNeedsSnprintf(statement, strategy) {
|
|
431
393
|
if (!strategy.useSnprintfForStrings()) {
|
|
432
394
|
return false;
|
|
@@ -171,10 +171,6 @@ export declare class StatementRenderer {
|
|
|
171
171
|
mapReturnTypeForEmit(fnName: string, returnType: string): string;
|
|
172
172
|
private normalizeCppType;
|
|
173
173
|
private inferFieldType;
|
|
174
|
-
/**
|
|
175
|
-
* Transform console.log/error/warn calls based on target platform.
|
|
176
|
-
*/
|
|
177
|
-
private transformConsoleCall;
|
|
178
174
|
private renderEmitArg;
|
|
179
175
|
/**
|
|
180
176
|
* Map a function name to its platform-specific name.
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { routeHALOp } from "./route-hal-op.js";
|
|
7
7
|
import { activeNamespaceNames } from "../ir/build-ir-state.js";
|
|
8
8
|
import { ExpressionRenderer, transformTypeName, normalizeRawExpression } from "./expression-renderer.js";
|
|
9
|
-
import {
|
|
9
|
+
import { inferObjectFieldType, collectNestedStructDefs } from "./utils/index.js";
|
|
10
10
|
import { escapeCppKeyword, escapeTrailingMember } from "../utils/strings.js";
|
|
11
11
|
import { accessorGetterName, accessorSetterName } from "./utils/cpp-helpers.js";
|
|
12
12
|
import { parseCppType, bareType, parsedIsPointer, parsedIsVector, parsedElementString, parsedIsPlainStructType } from "../api/shared/cpp-type-ir.js";
|
|
@@ -270,7 +270,7 @@ export class StatementRenderer {
|
|
|
270
270
|
// `return nullptr;` / `return CUTTLEFISH_UNDEFINED;` — those are
|
|
271
271
|
// integer/pointer literals that won't convert to the struct type.
|
|
272
272
|
// Lower to a value-initialized `return {};` instead. The `T | null`
|
|
273
|
-
// union already strips to `T
|
|
273
|
+
// union already strips to `T`, so the function
|
|
274
274
|
// signature really does return the struct. Demo #14 Finding A.
|
|
275
275
|
const retType = statement.functionReturnType;
|
|
276
276
|
if (retType && isStructReturnType(retType, this.strategy) && isNullishReturnValue(statement.value)) {
|
|
@@ -383,29 +383,46 @@ export class StatementRenderer {
|
|
|
383
383
|
return "co_yield;";
|
|
384
384
|
}
|
|
385
385
|
if (statement.kind === "block") {
|
|
386
|
-
|
|
386
|
+
// Blocks reach the single-line renderer as async state-machine segment
|
|
387
|
+
// pre-statements (chained HAL calls lower to a block of hal-ops). The
|
|
388
|
+
// former bare `{` dropped the block body AND emitted an unbalanced
|
|
389
|
+
// brace. Mirrors the line-appender's block emission: empty body →
|
|
390
|
+
// no-op, otherwise brace + rendered statements.
|
|
391
|
+
const bodyLines = statement.body
|
|
392
|
+
.map((nested) => this.render(nested, forHeader, calleeTransformer, knownVariableTypes))
|
|
393
|
+
.filter((line) => line.length > 0);
|
|
394
|
+
if (bodyLines.length === 0) {
|
|
395
|
+
return "";
|
|
396
|
+
}
|
|
397
|
+
return ["{", ...bodyLines, "}"].join("\n");
|
|
387
398
|
}
|
|
388
399
|
if (statement.kind === "hal-op") {
|
|
389
400
|
const resolved = routeHALOp(statement.operation, this.strategy);
|
|
390
|
-
if (resolved
|
|
391
|
-
//
|
|
392
|
-
//
|
|
393
|
-
//
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
401
|
+
if (resolved) {
|
|
402
|
+
// `{ code: '' }` is a registered op's deliberate elision (sentinel
|
|
403
|
+
// facts — an absent request body, insecure=false) — emit nothing and
|
|
404
|
+
// do NOT fall through to the unregistered-op warning.
|
|
405
|
+
if (resolved.code) {
|
|
406
|
+
// Strip leading 'return ' from raw hal-op code when emitted as a
|
|
407
|
+
// standalone statement. The HAL definition includes `return` because
|
|
408
|
+
// the TypeScript stub returns a value, but the C++ statement context
|
|
409
|
+
// (e.g. inside void setup()) does not expect it.
|
|
410
|
+
let code = resolved.code;
|
|
411
|
+
if (code.startsWith('return '))
|
|
412
|
+
code = code.slice('return '.length);
|
|
413
|
+
// A raw hal-op statement (e.g. `Async.sleep(10)` →
|
|
414
|
+
// `__cuttlefish_async_sleep(10)`) is a complete C++ statement and
|
|
415
|
+
// needs a terminating semicolon, unless it already ends with one or
|
|
416
|
+
// with `}` (a compound block).
|
|
417
|
+
if (!forHeader && !code.endsWith(';') && !code.endsWith('}')) {
|
|
418
|
+
code = code + ';';
|
|
419
|
+
}
|
|
420
|
+
return code;
|
|
404
421
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
return
|
|
422
|
+
if (resolved.expression) {
|
|
423
|
+
return forHeader ? resolved.expression : `${resolved.expression};`;
|
|
424
|
+
}
|
|
425
|
+
return "";
|
|
409
426
|
}
|
|
410
427
|
// Unregistered HAL op: surface as a warning so the user sees it, but
|
|
411
428
|
// keep HAL as an extensibility point. The bare comment is retained as
|
|
@@ -506,6 +523,11 @@ export class StatementRenderer {
|
|
|
506
523
|
if (code) {
|
|
507
524
|
return forHeader ? code.replace(/;$/, "") : code;
|
|
508
525
|
}
|
|
526
|
+
// A resolved op with no code/expression is a deliberate elision
|
|
527
|
+
// (sentinel facts) — emit nothing, no placeholder comment.
|
|
528
|
+
if (resolved) {
|
|
529
|
+
return "";
|
|
530
|
+
}
|
|
509
531
|
return `/* unhandled awaited hal-op: ${statement.args[0].operation.operation} */`;
|
|
510
532
|
}
|
|
511
533
|
// Handle emit() — compile-time C++ injection
|
|
@@ -513,9 +535,16 @@ export class StatementRenderer {
|
|
|
513
535
|
const rawText = statement.args.map(arg => this.renderEmitArg(arg)).join("");
|
|
514
536
|
return forHeader ? rawText : `${rawText.endsWith(';') ? rawText : rawText + ';'}`;
|
|
515
537
|
}
|
|
516
|
-
//
|
|
517
|
-
|
|
518
|
-
|
|
538
|
+
// console.* is not a supported API — it was a TypeScript carry-over whose
|
|
539
|
+
// lowering (platform print + a config section routing it) cost more than
|
|
540
|
+
// it bought. Programs write to a serial console explicitly instead.
|
|
541
|
+
if (statement.callee.startsWith("console.")) {
|
|
542
|
+
this._diagnostics.push({
|
|
543
|
+
severity: "error",
|
|
544
|
+
code: "console-unsupported",
|
|
545
|
+
message: `console.${statement.callee.slice("console.".length)}() is not supported — write to a serial console instead: \`USB0.writeLine(...)\` (USB CDC) or \`UART0.writeLine(...)\` from the board module.`,
|
|
546
|
+
});
|
|
547
|
+
return "";
|
|
519
548
|
}
|
|
520
549
|
let callee = statement.callee;
|
|
521
550
|
if (callee.startsWith("this.")) {
|
|
@@ -781,7 +810,7 @@ export class StatementRenderer {
|
|
|
781
810
|
? `${arrayType} ${safeSpreadArrName}[] = { ${initializerText} }`
|
|
782
811
|
: `${arrayType} ${safeSpreadArrName}[] = { ${initializerText} };`;
|
|
783
812
|
}
|
|
784
|
-
// Enum ↔ integral storage boundary
|
|
813
|
+
// Enum ↔ integral storage boundary. TS lets an
|
|
785
814
|
// enum and a number flow into each other freely (enums ARE numbers at
|
|
786
815
|
// runtime), but the lowered C++ `enum class` has NO implicit conversion
|
|
787
816
|
// in EITHER direction: `const n: int = Color.Red` AND
|
|
@@ -917,26 +946,6 @@ export class StatementRenderer {
|
|
|
917
946
|
return inferObjectFieldType(value, this.pointerVarTypes, this.knownFunctionReturnTypes, undefined, undefined, undefined, // largeEnumNames - would need to pass through
|
|
918
947
|
parentName, fieldName, this.strategy.defaultNumericType(this._compliance), (o, n) => this.strategy.resolvePinType?.(o, n));
|
|
919
948
|
}
|
|
920
|
-
/**
|
|
921
|
-
* Transform console.log/error/warn calls based on target platform.
|
|
922
|
-
*/
|
|
923
|
-
transformConsoleCall(callee, args, forHeader, knownVariableTypes) {
|
|
924
|
-
const method = getConsoleMethod(callee);
|
|
925
|
-
const renderedArgs = args.map((arg) => {
|
|
926
|
-
let rendered = this.expressionRenderer.render(arg, undefined, knownVariableTypes);
|
|
927
|
-
if (arg.kind === "identifier" && knownVariableTypes) {
|
|
928
|
-
const varInfo = knownVariableTypes.get(arg.value);
|
|
929
|
-
if (varInfo && this.enumNames.has(varInfo.cppType)) {
|
|
930
|
-
rendered = `static_cast<int>(${rendered})`;
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
if (arg.kind === "property-access" && arg.isEnum) {
|
|
934
|
-
rendered = `static_cast<int>(${rendered})`;
|
|
935
|
-
}
|
|
936
|
-
return rendered;
|
|
937
|
-
}).join(" << ");
|
|
938
|
-
return this.strategy.transformConsoleCall(method, renderedArgs, forHeader);
|
|
939
|
-
}
|
|
940
949
|
renderEmitArg(arg) {
|
|
941
950
|
if (arg.kind === "string")
|
|
942
951
|
return arg.value;
|
|
@@ -5,14 +5,6 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { StatementIR } from "../../api/index.js";
|
|
7
7
|
import type { PlatformStrategy } from "../../api/shared/index.js";
|
|
8
|
-
/**
|
|
9
|
-
* Converts a string to PascalCase.
|
|
10
|
-
* Used for generating class names from function names.
|
|
11
|
-
*
|
|
12
|
-
* @param str The input string (e.g., "my_function" or "myFunction")
|
|
13
|
-
* @returns PascalCase string (e.g., "MyFunction")
|
|
14
|
-
*/
|
|
15
|
-
export declare function toPascalCaseLocal(str: string): string;
|
|
16
8
|
/**
|
|
17
9
|
* Result of generating an async task class.
|
|
18
10
|
*/
|
|
@@ -23,6 +15,21 @@ export interface AsyncTaskClassResult {
|
|
|
23
15
|
instanceDecl: string;
|
|
24
16
|
/** The task variable name for use in loop() */
|
|
25
17
|
taskVarName: string;
|
|
18
|
+
/** For async METHODS: the definition of the starter function the in-class
|
|
19
|
+
* body calls (binds the owner and arms STATE_0). Emitted after the task
|
|
20
|
+
* class; the matching prototype must precede the owning class. */
|
|
21
|
+
starterDef?: string;
|
|
22
|
+
}
|
|
23
|
+
/** Options for generateAsyncTaskClass. */
|
|
24
|
+
export interface AsyncTaskClassOptions {
|
|
25
|
+
/** Owning class name for async METHODS: the task binds `this` to an owner
|
|
26
|
+
* pointer set by start(owner); segment code that renders `this->x` is
|
|
27
|
+
* rewritten to `_owner->x` by the embedding renderStatement callback. */
|
|
28
|
+
ownerClassName?: string;
|
|
29
|
+
/** Invoked for every awaited call that has no cooperative lowering (the
|
|
30
|
+
* call would be silently dropped). The emitter surfaces it as a
|
|
31
|
+
* diagnostic instead of emitting wrong code. */
|
|
32
|
+
onUnsupportedAwait?: (callee: string, span: StatementIR["sourceSpan"] | undefined) => void;
|
|
26
33
|
}
|
|
27
34
|
/**
|
|
28
35
|
* Generates a cooperative state-machine class for an async function.
|
|
@@ -39,4 +46,4 @@ export interface AsyncTaskClassResult {
|
|
|
39
46
|
* @param knownFunctionReturnTypes Map of function names to their return types
|
|
40
47
|
* @returns The class definition, instance declaration, and task variable name
|
|
41
48
|
*/
|
|
42
|
-
export declare function generateAsyncTaskClass(fnName: string, fnStatements: StatementIR[], strategy: PlatformStrategy, knownFunctionReturnTypes: Map<string, string>, renderStatement: (stmt: StatementIR, forHeader: boolean, strategy: PlatformStrategy, pointerVarTypes?: Map<string, string>, calleeTransformer?: (callee: string) => string, knownFunctionReturnTypes?: Map<string, string>) => string): AsyncTaskClassResult;
|
|
49
|
+
export declare function generateAsyncTaskClass(fnName: string, fnStatements: StatementIR[], strategy: PlatformStrategy, knownFunctionReturnTypes: Map<string, string>, renderStatement: (stmt: StatementIR, forHeader: boolean, strategy: PlatformStrategy, pointerVarTypes?: Map<string, string>, calleeTransformer?: (callee: string) => string, knownFunctionReturnTypes?: Map<string, string>) => string, options?: AsyncTaskClassOptions): AsyncTaskClassResult;
|
|
@@ -12,12 +12,23 @@ import { routeHALOp } from "../route-hal-op.js";
|
|
|
12
12
|
* @param str The input string (e.g., "my_function" or "myFunction")
|
|
13
13
|
* @returns PascalCase string (e.g., "MyFunction")
|
|
14
14
|
*/
|
|
15
|
-
|
|
15
|
+
function toPascalCaseLocal(str) {
|
|
16
16
|
return str
|
|
17
17
|
.split(/[_\s]+/)
|
|
18
18
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
19
19
|
.join("");
|
|
20
20
|
}
|
|
21
|
+
/** Awaited callees that legitimately lower to a plain timed wait (their
|
|
22
|
+
* single numeric argument IS the deadline). Everything else reaching the
|
|
23
|
+
* plain-wait arm would have its call silently dropped — the embedding
|
|
24
|
+
* emitter turns that into a diagnostic via onUnsupportedAwait. */
|
|
25
|
+
const PLAIN_AWAIT_CALLEES = new Set([
|
|
26
|
+
"Async.sleep",
|
|
27
|
+
"AsyncClass.sleep",
|
|
28
|
+
"__cuttlefish_async_sleep",
|
|
29
|
+
"delay",
|
|
30
|
+
"sleep",
|
|
31
|
+
]);
|
|
21
32
|
/**
|
|
22
33
|
* Generates a cooperative state-machine class for an async function.
|
|
23
34
|
*
|
|
@@ -33,9 +44,11 @@ export function toPascalCaseLocal(str) {
|
|
|
33
44
|
* @param knownFunctionReturnTypes Map of function names to their return types
|
|
34
45
|
* @returns The class definition, instance declaration, and task variable name
|
|
35
46
|
*/
|
|
36
|
-
export function generateAsyncTaskClass(fnName, fnStatements, strategy, knownFunctionReturnTypes, renderStatement) {
|
|
47
|
+
export function generateAsyncTaskClass(fnName, fnStatements, strategy, knownFunctionReturnTypes, renderStatement, options) {
|
|
37
48
|
const className = toPascalCaseLocal(fnName) + "Task";
|
|
38
49
|
const instanceName = `${fnName}Task`;
|
|
50
|
+
const owner = options?.ownerClassName;
|
|
51
|
+
const starterName = `__tc_async_start_${fnName}`;
|
|
39
52
|
// Detect whether the body is a single while-loop (cyclic) or linear statements
|
|
40
53
|
let bodyStatements;
|
|
41
54
|
let isCyclic = false;
|
|
@@ -51,7 +64,7 @@ export function generateAsyncTaskClass(fnName, fnStatements, strategy, knownFunc
|
|
|
51
64
|
let currentPre = [];
|
|
52
65
|
for (const stmt of bodyStatements) {
|
|
53
66
|
if (stmt.kind === "call" && stmt.isAwaited) {
|
|
54
|
-
segments.push({ preStatements: currentPre, awaitedCallee: stmt.callee, awaitedArgs: stmt.args });
|
|
67
|
+
segments.push({ preStatements: currentPre, awaitedCallee: stmt.callee, awaitedArgs: stmt.args, awaitedSpan: stmt.sourceSpan });
|
|
55
68
|
currentPre = [];
|
|
56
69
|
}
|
|
57
70
|
else if (
|
|
@@ -65,7 +78,7 @@ export function generateAsyncTaskClass(fnName, fnStatements, strategy, knownFunc
|
|
|
65
78
|
stmt.body[stmt.body.length - 1].isAwaited) {
|
|
66
79
|
const last = stmt.body[stmt.body.length - 1];
|
|
67
80
|
currentPre.push(...stmt.body.slice(0, -1));
|
|
68
|
-
segments.push({ preStatements: currentPre, awaitedCallee: last.callee, awaitedArgs: last.args });
|
|
81
|
+
segments.push({ preStatements: currentPre, awaitedCallee: last.callee, awaitedArgs: last.args, awaitedSpan: last.sourceSpan });
|
|
69
82
|
currentPre = [];
|
|
70
83
|
}
|
|
71
84
|
else {
|
|
@@ -173,7 +186,13 @@ export function generateAsyncTaskClass(fnName, fnStatements, strategy, knownFunc
|
|
|
173
186
|
}
|
|
174
187
|
}
|
|
175
188
|
else {
|
|
176
|
-
// Plain timed wait (await delay(ms)) — arm its deadline.
|
|
189
|
+
// Plain timed wait (await delay(ms)) — arm its deadline. Anything whose
|
|
190
|
+
// callee is not a known timer shape has no cooperative lowering here:
|
|
191
|
+
// the call itself is NOT rendered (only marker/net awaits carry start
|
|
192
|
+
// code), so report it instead of silently dropping it.
|
|
193
|
+
if (!PLAIN_AWAIT_CALLEES.has(seg.awaitedCallee)) {
|
|
194
|
+
options?.onUnsupportedAwait?.(seg.awaitedCallee, seg.awaitedSpan);
|
|
195
|
+
}
|
|
177
196
|
const ms = seg.awaitedArgs[0] ? renderExpression(seg.awaitedArgs[0], strategy) : "0";
|
|
178
197
|
lines.push(`${pad}_waitUntil = ${strategy.currentTimeMillis()} + ${ms};`);
|
|
179
198
|
}
|
|
@@ -265,19 +284,37 @@ export function generateAsyncTaskClass(fnName, fnStatements, strategy, knownFunc
|
|
|
265
284
|
inits.push(`${m}(LOW)`);
|
|
266
285
|
for (const m of tapMemberArr)
|
|
267
286
|
inits.push(`${m}(0)`);
|
|
287
|
+
// Owner pointer (async methods): initialized null, bound by start(owner).
|
|
288
|
+
// Declared last / initialized last so the initializer list order matches.
|
|
289
|
+
if (owner)
|
|
290
|
+
inits.push("_owner(nullptr)");
|
|
268
291
|
const ctorInitList = inits.join(", ");
|
|
269
292
|
const edgeResetList = edgeMemberArr.map(m => ` ${m} = LOW;`).join("");
|
|
270
293
|
const tapResetList = tapMemberArr.map(m => ` ${m} = 0;`).join("");
|
|
271
294
|
const edgeMemberDecls = edgeMemberArr.map(m => ` int ${m};`);
|
|
272
295
|
// __ui_tap_seq is uint32_t; the snapshot must match to detect bumps correctly.
|
|
273
296
|
const tapMemberDecls = tapMemberArr.map(m => ` uint32_t ${m};`);
|
|
297
|
+
const ownerMemberDecl = owner ? [` ${owner}* _owner;`] : [];
|
|
298
|
+
const runGuard = owner ? [` if (_owner == nullptr) { return; }`] : [];
|
|
299
|
+
// start(owner): (re)bind the receiver and rewind the machine. Calling the
|
|
300
|
+
// async method again restarts the task with the new receiver — the task
|
|
301
|
+
// is a singleton per method, mirroring the free-function tasks.
|
|
302
|
+
const startMethod = owner ? [
|
|
303
|
+
` void start(${owner}* owner) {`,
|
|
304
|
+
` _owner = owner;`,
|
|
305
|
+
` _state = ${Q_0};`,
|
|
306
|
+
` _waitUntil = 0;${edgeResetList}${tapResetList}`,
|
|
307
|
+
` }`,
|
|
308
|
+
] : [];
|
|
274
309
|
const classDef = [
|
|
275
310
|
`// Async state machine for ${fnName}`,
|
|
276
311
|
`class ${className} {`,
|
|
277
312
|
`public:`,
|
|
278
313
|
` enum class State { ${stateEnumList} };`,
|
|
279
314
|
` ${className}() : ${ctorInitList} {}`,
|
|
315
|
+
...startMethod,
|
|
280
316
|
` void run() {`,
|
|
317
|
+
...runGuard,
|
|
281
318
|
` switch (_state) {`,
|
|
282
319
|
...caseLines,
|
|
283
320
|
` }`,
|
|
@@ -289,9 +326,13 @@ export function generateAsyncTaskClass(fnName, fnStatements, strategy, knownFunc
|
|
|
289
326
|
` unsigned long _waitUntil;`,
|
|
290
327
|
...edgeMemberDecls,
|
|
291
328
|
...tapMemberDecls,
|
|
329
|
+
...ownerMemberDecl,
|
|
292
330
|
`};`,
|
|
293
331
|
].join("\n");
|
|
294
|
-
|
|
332
|
+
const starterDef = owner
|
|
333
|
+
? `void ${starterName}(${owner}* owner) { ${instanceName}.start(owner); }`
|
|
334
|
+
: undefined;
|
|
335
|
+
return { classDef, instanceDecl: `${className} ${instanceName};`, taskVarName: instanceName, starterDef };
|
|
295
336
|
}
|
|
296
337
|
/**
|
|
297
338
|
* Renders an expression to a string (simplified version for async state machine).
|
|
@@ -346,10 +387,16 @@ function netWaitInfo(op, strategy) {
|
|
|
346
387
|
const route = (routedOp) => routeHALOp(routedOp, strategy);
|
|
347
388
|
const expr = (operation) => route({ operation })?.expression ?? null;
|
|
348
389
|
switch (op.operation) {
|
|
349
|
-
case "timing.
|
|
390
|
+
case "timing.sleep":
|
|
391
|
+
// Pure deadline wait: no start op, no poll — the machine arms
|
|
392
|
+
// _waitUntil and cooperatively yields until it passes. Bare
|
|
393
|
+
// (non-awaited) calls lower to the blocking form (k_msleep).
|
|
350
394
|
return { startLines: [], pollCond: null, timeoutExpr: ms(o.ms) };
|
|
351
|
-
case "wifi.
|
|
352
|
-
|
|
395
|
+
case "wifi.join": {
|
|
396
|
+
// Awaited join splits into the staged association + the L4 poll.
|
|
397
|
+
// (Static-IPv4/power-save facts are join()-side blocking-path extras;
|
|
398
|
+
// the async form associates with defaults and polls connectivity.)
|
|
399
|
+
const start = route({ operation: "wifi.connect_start", ssid: o.ssid, password: o.psk });
|
|
353
400
|
const poll = expr("wifi.is_connected");
|
|
354
401
|
if (start?.code && poll) {
|
|
355
402
|
return {
|
|
@@ -360,24 +407,6 @@ function netWaitInfo(op, strategy) {
|
|
|
360
407
|
}
|
|
361
408
|
break;
|
|
362
409
|
}
|
|
363
|
-
case "wifi.wait_connected": {
|
|
364
|
-
const poll = expr("wifi.is_connected");
|
|
365
|
-
if (poll) {
|
|
366
|
-
return {
|
|
367
|
-
startLines: [],
|
|
368
|
-
pollCond: poll,
|
|
369
|
-
timeoutExpr: isZeroTimeout(o.timeoutMs) ? null : ms(o.timeoutMs),
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
break;
|
|
373
|
-
}
|
|
374
|
-
case "wifi.wait_disconnected": {
|
|
375
|
-
const poll = expr("wifi.is_connected");
|
|
376
|
-
if (poll) {
|
|
377
|
-
return { startLines: [], pollCond: `!${poll}`, timeoutExpr: null };
|
|
378
|
-
}
|
|
379
|
-
break;
|
|
380
|
-
}
|
|
381
410
|
case "wifi.scan": {
|
|
382
411
|
const start = route({ operation: "wifi.scan_start" });
|
|
383
412
|
const poll = expr("wifi.scan_done");
|
|
@@ -394,33 +423,6 @@ function netWaitInfo(op, strategy) {
|
|
|
394
423
|
}
|
|
395
424
|
break;
|
|
396
425
|
}
|
|
397
|
-
case "ble.until_connected": {
|
|
398
|
-
const start = route({ operation: "ble.until_connected_start" });
|
|
399
|
-
const poll = expr("ble.is_connected");
|
|
400
|
-
if (start?.code && poll) {
|
|
401
|
-
return {
|
|
402
|
-
startLines: [start.code],
|
|
403
|
-
pollCond: poll,
|
|
404
|
-
timeoutExpr: isZeroTimeout(o.timeoutMs) ? null : ms(o.timeoutMs),
|
|
405
|
-
};
|
|
406
|
-
}
|
|
407
|
-
break;
|
|
408
|
-
}
|
|
409
|
-
case "worker.submit": {
|
|
410
|
-
// Worker offload (Phase 3). `await worker.submit(fn, arg)` rewrites to:
|
|
411
|
-
// submit the worker (start), then poll worker.done on the same handle.
|
|
412
|
-
// The submit op is emitted verbatim as the start line; the poll predicate
|
|
413
|
-
// is worker.done(handle) carrying the SAME handleId. This is a predicate-
|
|
414
|
-
// poll resume (same per-frame cost as net/timer awaits), NOT an ISR-driven
|
|
415
|
-
// resume — that defers with DMA-backed awaits.
|
|
416
|
-
const submit = routeHALOp(op, strategy);
|
|
417
|
-
// Carry the handle through to the poll so submit + done agree on the slot.
|
|
418
|
-
const poll = route({ operation: "worker.done", handleId: o.handleId })?.expression ?? null;
|
|
419
|
-
if (submit?.code && poll) {
|
|
420
|
-
return { startLines: [submit.code], pollCond: poll, timeoutExpr: null };
|
|
421
|
-
}
|
|
422
|
-
break;
|
|
423
|
-
}
|
|
424
426
|
}
|
|
425
427
|
// Fallback: run the blocking form immediately and complete on the next tick.
|
|
426
428
|
const blocking = routeHALOp(op, strategy);
|
|
@@ -2,14 +2,6 @@
|
|
|
2
2
|
* Comment handling utilities for C++ code emission.
|
|
3
3
|
* Extracted from cpp-emitter.ts
|
|
4
4
|
*/
|
|
5
|
-
/**
|
|
6
|
-
* Normalizes a comment string into an array of trimmed lines.
|
|
7
|
-
* Removes empty lines and trailing whitespace.
|
|
8
|
-
*
|
|
9
|
-
* @param comment The comment string (may contain newlines)
|
|
10
|
-
* @returns Array of normalized comment lines
|
|
11
|
-
*/
|
|
12
|
-
export declare function normalizeComment(comment: string): string[];
|
|
13
5
|
/**
|
|
14
6
|
* Emits comment lines with proper indentation.
|
|
15
7
|
*
|