@typecad/cuttlefish 1.0.0-alpha.6 → 1.0.0-alpha.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/dist/api/config.d.ts +32 -0
  2. package/dist/api/index.d.ts +1 -1
  3. package/dist/api/schema/types.d.ts +15 -0
  4. package/dist/api/shared/async-runtime-static.d.ts +12 -1
  5. package/dist/api/shared/async-runtime-static.js +69 -24
  6. package/dist/api/shared/async-symbol-detector.d.ts +11 -0
  7. package/dist/api/shared/async-symbol-detector.js +140 -0
  8. package/dist/api/shared/async-types.d.ts +24 -0
  9. package/dist/api/shared/coop-scheduler.d.ts +60 -0
  10. package/dist/api/shared/coop-scheduler.js +149 -0
  11. package/dist/api/shared/display-adapter.d.ts +4 -3
  12. package/dist/api/shared/display-adapter.js +15 -118
  13. package/dist/api/shared/display-adapters/sdl.js +38 -31
  14. package/dist/api/shared/display-profile.d.ts +47 -25
  15. package/dist/api/shared/display-profile.js +31 -164
  16. package/dist/api/shared/framework-manifest-registry.d.ts +9 -0
  17. package/dist/api/shared/framework-manifest-registry.js +27 -0
  18. package/dist/api/shared/framework-manifest.d.ts +581 -0
  19. package/dist/api/shared/framework-manifest.js +182 -0
  20. package/dist/api/shared/glcdfont.d.ts +12 -0
  21. package/dist/api/shared/glcdfont.js +124 -0
  22. package/dist/api/shared/graphics-strategy.d.ts +37 -0
  23. package/dist/api/shared/hal-op-ir.d.ts +755 -1
  24. package/dist/api/shared/hal-op-ir.js +129 -1
  25. package/dist/api/shared/index.d.ts +20 -2
  26. package/dist/api/shared/index.js +25 -1
  27. package/dist/api/shared/ir-core.d.ts +4 -0
  28. package/dist/api/shared/ir-declarations.d.ts +6 -0
  29. package/dist/api/shared/native-display-op-resolver.d.ts +10 -0
  30. package/dist/api/shared/native-display-op-resolver.js +64 -0
  31. package/dist/api/shared/platform-strategy.d.ts +113 -2
  32. package/dist/api/shared/polyfill-helper-registry.js +18 -1
  33. package/dist/api/shared/promise-runtime.d.ts +1 -1
  34. package/dist/api/shared/promise-runtime.js +95 -13
  35. package/dist/api/shared/toolchain-types.d.ts +17 -0
  36. package/dist/api/shared/validate-framework-manifest.d.ts +28 -0
  37. package/dist/api/shared/validate-framework-manifest.js +494 -0
  38. package/dist/api/shared/worker-runtime-polyfill.d.ts +22 -0
  39. package/dist/api/shared/worker-runtime-polyfill.js +34 -0
  40. package/dist/api/shared/worker-runtime.d.ts +69 -0
  41. package/dist/api/shared/worker-runtime.js +163 -0
  42. package/dist/cli.js +186 -82
  43. package/dist/config-loader.d.ts +7 -2
  44. package/dist/config-loader.js +23 -3
  45. package/dist/config-schema.d.ts +112 -70
  46. package/dist/config-schema.js +14 -0
  47. package/dist/contract/board-generator.d.ts +17 -0
  48. package/dist/contract/board-generator.js +57 -0
  49. package/dist/contract/contract-parser.d.ts +217 -0
  50. package/dist/contract/contract-parser.js +224 -0
  51. package/dist/contract/index.d.ts +22 -0
  52. package/dist/contract/index.js +68 -0
  53. package/dist/create/board-codegen.js +4 -4
  54. package/dist/create/board-generators.js +4 -5
  55. package/dist/create/board-spec.d.ts +72 -75
  56. package/dist/create/board-spec.js +0 -1
  57. package/dist/create/init-scaffold.d.ts +9 -2
  58. package/dist/create/init-scaffold.js +0 -14
  59. package/dist/create/init-templates.d.ts +2 -0
  60. package/dist/create/init-templates.js +74 -8
  61. package/dist/create/init-wizard.js +31 -7
  62. package/dist/debug/preprocessor.js +178 -39
  63. package/dist/debug/types.d.ts +23 -0
  64. package/dist/diagnostics/mermaid-builder.d.ts +1 -1
  65. package/dist/diagnostics/mermaid-builder.js +34 -24
  66. package/dist/emit/compliance/arxml-writer.d.ts +11 -0
  67. package/dist/emit/compliance/arxml-writer.js +34 -0
  68. package/dist/emit/compliance/compliance-context.d.ts +57 -0
  69. package/dist/emit/compliance/compliance-context.js +113 -0
  70. package/dist/emit/compliance/deviation-ledger.d.ts +27 -0
  71. package/dist/emit/compliance/deviation-ledger.js +47 -0
  72. package/dist/emit/compliance/deviation-writer.d.ts +30 -0
  73. package/dist/emit/compliance/deviation-writer.js +37 -0
  74. package/dist/emit/compliance/index.d.ts +7 -0
  75. package/dist/emit/compliance/index.js +6 -0
  76. package/dist/emit/compliance/rule-engine.d.ts +13 -0
  77. package/dist/emit/compliance/rule-engine.js +101 -0
  78. package/dist/emit/compliance/rules.d.ts +16 -0
  79. package/dist/emit/compliance/rules.js +191 -0
  80. package/dist/emit/compliance/types.d.ts +59 -0
  81. package/dist/emit/compliance/types.js +8 -0
  82. package/dist/emit/cpp-emitter.js +4 -3
  83. package/dist/emit/emitters/class-emitter.js +6 -1
  84. package/dist/emit/emitters/emitter-context.d.ts +28 -1
  85. package/dist/emit/emitters/function-emitter-impl.js +153 -53
  86. package/dist/emit/emitters/line-appender.js +19 -0
  87. package/dist/emit/emitters/line-marker.d.ts +38 -0
  88. package/dist/emit/emitters/line-marker.js +39 -0
  89. package/dist/emit/emitters/output-finalizer.d.ts +6 -0
  90. package/dist/emit/emitters/output-finalizer.js +101 -12
  91. package/dist/emit/emitters/setup.d.ts +17 -0
  92. package/dist/emit/emitters/setup.js +257 -36
  93. package/dist/emit/emitters/top-level-prep.js +8 -0
  94. package/dist/emit/emitters/type-decl-emitter.js +28 -3
  95. package/dist/emit/emitters/ui-emitter.js +33 -9
  96. package/dist/emit/expression-renderer.d.ts +1 -1
  97. package/dist/emit/expression-renderer.js +91 -6
  98. package/dist/emit/route-hal-op.js +18 -5
  99. package/dist/emit/snprintf-helpers.js +15 -4
  100. package/dist/emit/statement-renderer.d.ts +10 -0
  101. package/dist/emit/statement-renderer.js +34 -5
  102. package/dist/emit/utils/async-state-machine.js +221 -125
  103. package/dist/emit/utils/hal-op-cpp-type.d.ts +6 -0
  104. package/dist/emit/utils/hal-op-cpp-type.js +40 -0
  105. package/dist/framework-package.js +2 -0
  106. package/dist/framework-registry.d.ts +17 -0
  107. package/dist/ir/adc-range-validation.js +14 -1
  108. package/dist/ir/build-ir-state.d.ts +1 -0
  109. package/dist/ir/build-ir-state.js +14 -0
  110. package/dist/ir/build-ir.js +9 -5
  111. package/dist/ir/call-graph.js +16 -0
  112. package/dist/ir/expression-to-ir.js +164 -0
  113. package/dist/ir/feature-registry.js +7 -25
  114. package/dist/ir/function-builder.js +22 -0
  115. package/dist/ir/hal/hal-emitter.d.ts +5 -2
  116. package/dist/ir/hal/hal-emitter.js +50 -18
  117. package/dist/ir/hal/hal-parser.d.ts +6 -0
  118. package/dist/ir/hal/hal-parser.js +81 -0
  119. package/dist/ir/hal/hal-plugins.js +723 -1
  120. package/dist/ir/identifier-collector.js +35 -0
  121. package/dist/ir/interrupt-analysis.d.ts +5 -1
  122. package/dist/ir/interrupt-analysis.js +8 -16
  123. package/dist/ir/network-validation.d.ts +4 -0
  124. package/dist/ir/network-validation.js +184 -0
  125. package/dist/ir/ownership-analysis.js +20 -1
  126. package/dist/ir/pin-capability-validation.js +37 -0
  127. package/dist/ir/pin-mode-validation.d.ts +2 -2
  128. package/dist/ir/pin-mode-validation.js +49 -18
  129. package/dist/ir/program-analysis.d.ts +65 -0
  130. package/dist/ir/program-analysis.js +318 -2
  131. package/dist/ir/render-expr.js +11 -0
  132. package/dist/ir/timing-validation.d.ts +6 -1
  133. package/dist/ir/timing-validation.js +60 -13
  134. package/dist/ir/transformers/call-statement.js +105 -0
  135. package/dist/ir/transformers/expressions.js +62 -0
  136. package/dist/ir/transformers/hal-call-resolver.js +21 -0
  137. package/dist/ir/transformers/hal-emit-helpers.js +1 -1
  138. package/dist/ir/transformers/namespace-methods.js +17 -12
  139. package/dist/ir/transformers/ui-reactive.js +2 -2
  140. package/dist/ir/transformers/variables.js +92 -3
  141. package/dist/ir/type-resolution.js +18 -0
  142. package/dist/ir/ui-element-auto-wire.js +7 -5
  143. package/dist/ir/utils/map-statements.d.ts +4 -0
  144. package/dist/ir/utils/map-statements.js +79 -0
  145. package/dist/ir/validation-orchestrator.js +9 -2
  146. package/dist/ir/worker-analysis.d.ts +10 -0
  147. package/dist/ir/worker-analysis.js +261 -0
  148. package/dist/libdef/c-to-decl.d.ts +27 -0
  149. package/dist/libdef/c-to-decl.js +397 -0
  150. package/dist/libdef/component-decls.d.ts +2 -0
  151. package/dist/libdef/component-decls.js +6 -0
  152. package/dist/libdef/component-discovery.d.ts +43 -0
  153. package/dist/libdef/component-discovery.js +83 -0
  154. package/dist/libdef/cpp-to-decl.d.ts +9 -0
  155. package/dist/libdef/cpp-to-decl.js +72 -0
  156. package/dist/libdef/registry.js +5 -2
  157. package/dist/lint-cache.d.ts +59 -0
  158. package/dist/lint-cache.js +257 -0
  159. package/dist/orchestrator/graph-builder.js +14 -9
  160. package/dist/platform/coop-scheduler-runtime.d.ts +19 -0
  161. package/dist/platform/coop-scheduler-runtime.js +41 -0
  162. package/dist/platform/generic-strategy.d.ts +15 -3
  163. package/dist/platform/generic-strategy.js +49 -4
  164. package/dist/safety/safety-bridge.d.ts +11 -0
  165. package/dist/safety/safety-bridge.js +48 -0
  166. package/dist/safety/sidecar-bridge.d.ts +5 -0
  167. package/dist/safety/sidecar-bridge.js +22 -0
  168. package/dist/safety-hook.d.ts +79 -0
  169. package/dist/safety-hook.js +35 -0
  170. package/dist/testing.d.ts +7 -1
  171. package/dist/testing.js +8 -1
  172. package/dist/transpile.d.ts +3 -0
  173. package/dist/transpile.js +175 -41
  174. package/dist/types.d.ts +13 -2
  175. package/dist/ui-hook.d.ts +17 -3
  176. package/dist/utils/cli.js +78 -4
  177. package/dist/utils/fs.d.ts +13 -0
  178. package/dist/utils/fs.js +50 -0
  179. package/package.json +24 -5
  180. package/dist/api/shared/display-adapters/eink-mono.d.ts +0 -2
  181. package/dist/api/shared/display-adapters/eink-mono.js +0 -53
  182. package/dist/api/shared/display-adapters/ssd1309.d.ts +0 -2
  183. package/dist/api/shared/display-adapters/ssd1309.js +0 -136
  184. package/dist/api/shared/display-adapters/st7796.d.ts +0 -2
  185. package/dist/api/shared/display-adapters/st7796.js +0 -132
  186. package/dist/licenses.d.ts +0 -185
  187. package/dist/licenses.js +0 -963
@@ -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
  */
@@ -79,6 +80,18 @@ export class ExpressionRenderer {
79
80
  if (!expr || typeof expr !== 'object' || !expr.kind) {
80
81
  return "/* invalid expression */";
81
82
  }
83
+ // When a per-scope knownVariableTypes is passed (e.g. a function's child
84
+ // emission scope carrying its parameters), install it on the instance for
85
+ // the duration of this render so every recursive render() call and
86
+ // renderIdentifier() reads the merged scope (globals + params). Without
87
+ // this, recursive paths that don't forward knownVariableTypes (ternary
88
+ // branches, call args, array elements, …) would resolve identifiers
89
+ // against only the top-level scope, missing function parameters — which
90
+ // breaks reserved-name escaping for params like `min`/`max` on Arduino.
91
+ const prevKnownVariableTypes = this.knownVariableTypes;
92
+ if (knownVariableTypes) {
93
+ this.knownVariableTypes = knownVariableTypes;
94
+ }
82
95
  let rendered;
83
96
  switch (expr.kind) {
84
97
  case "number": {
@@ -99,7 +112,7 @@ export class ExpressionRenderer {
99
112
  rendered = expr.value ? "true" : "false";
100
113
  break;
101
114
  case "identifier":
102
- rendered = this.renderIdentifier(expr.value);
115
+ rendered = this.renderIdentifier(expr.value, knownVariableTypes);
103
116
  break;
104
117
  case "raw":
105
118
  rendered = this.renderRaw(expr.value, exprTransformer);
@@ -149,6 +162,18 @@ export class ExpressionRenderer {
149
162
  case "method-call":
150
163
  rendered = this.renderMethodCall(expr, exprTransformer);
151
164
  break;
165
+ case "call": {
166
+ // Bare call expression: `Name(args)` with no receiver (e.g. a
167
+ // constructor call `SafeInt(x)` written inline, or a free function).
168
+ // Method-on-receiver calls lower as "method-call"; this arm is for the
169
+ // receiverless form. The callee is escaped against C++ reserved names;
170
+ // when a target type is in scope, renderValueForTarget (the caller)
171
+ // wraps this with template args (SafeInt(x) -> SafeInt<int32_t>(x)).
172
+ const callArgs = expr.args.map(a => this.render(a, exprTransformer)).join(", ");
173
+ const callCallee = escapeCppKeyword(exprTransformer ? exprTransformer(expr.callee) : expr.callee, this.strategy.reservedNames());
174
+ rendered = `${callCallee}(${callArgs})`;
175
+ break;
176
+ }
152
177
  case "element-access":
153
178
  rendered = this.renderElementAccess(expr, exprTransformer, knownVariableTypes);
154
179
  break;
@@ -191,9 +216,10 @@ export class ExpressionRenderer {
191
216
  `This is a transpiler bug; the renderer is missing a case for this kind.`);
192
217
  }
193
218
  }
219
+ this.knownVariableTypes = prevKnownVariableTypes;
194
220
  return normalizeRawExpression(rendered, this.strategy, this.classNameMap);
195
221
  }
196
- renderIdentifier(value) {
222
+ renderIdentifier(value, scopeKnownVariableTypes) {
197
223
  const nullVal = this.strategy.nullValue();
198
224
  // expression-to-ir lowers the TS `null` literal to the identifier sentinel
199
225
  // "nullptr" and `undefined` to "CUTTLEFISH_UNDEFINED". Both must route
@@ -209,7 +235,27 @@ export class ExpressionRenderer {
209
235
  if (this.strategy.passthroughMacroNames().has(value)) {
210
236
  return value;
211
237
  }
212
- return escapeCppKeyword(value, this.strategy.reservedNames());
238
+ // Platform-reserved names (e.g. Arduino `Serial`, or the `min`/`max` math
239
+ // macros) are globals/macros provided by the framework headers. Escaping
240
+ // them would break references to those globals — UNLESS the name is a
241
+ // user-declared variable/parameter that shadows and would collide. Check
242
+ // both the instance field (globals/locals seen so far) AND the per-scope
243
+ // map passed by the caller (function parameters, which live in the child
244
+ // emission scope and aren't on the instance field). Declaration sites
245
+ // already escape user vars (renderParameters → renderTypedName), so the
246
+ // body references must escape to match.
247
+ const reservedNames = this.strategy.reservedNames();
248
+ if (reservedNames.has(value)) {
249
+ const isUserVar = (this.knownVariableTypes !== undefined && this.knownVariableTypes.has(value)) ||
250
+ (scopeKnownVariableTypes !== undefined && scopeKnownVariableTypes.has(value)) ||
251
+ (this.pointerVarTypes !== undefined && this.pointerVarTypes.has(value)) ||
252
+ (this.globalPointerVarTypes !== undefined && this.globalPointerVarTypes.has(value)) ||
253
+ (this.stringVarNames !== undefined && this.stringVarNames.has(value));
254
+ if (!isUserVar) {
255
+ return value;
256
+ }
257
+ }
258
+ return escapeCppKeyword(value, reservedNames);
213
259
  }
214
260
  renderRaw(value, exprTransformer) {
215
261
  const effectiveClassNameMap = this.classNameMap;
@@ -403,6 +449,8 @@ export class ExpressionRenderer {
403
449
  }
404
450
  case "unary":
405
451
  return expr.operator === "!" ? "bool" : this.inferExpressionCppType(expr.operand, knownVariableTypes);
452
+ case "hal-expr":
453
+ return cppTypeForHalOp(expr.operation.operation);
406
454
  default:
407
455
  return undefined;
408
456
  }
@@ -714,10 +762,16 @@ export class ExpressionRenderer {
714
762
  }
715
763
  case "hal-expr": {
716
764
  const rendered = this.render(expr, exprTransformer);
717
- // HAL expressions that resolve to string-like outputs use %s
718
765
  if (rendered.startsWith('"') || this.stringVarNames?.has(rendered)) {
719
766
  return { format: "%s", arg: rendered, estimatedLength: 32 };
720
767
  }
768
+ const cppType = cppTypeForHalOp(expr.operation.operation);
769
+ if (cppType && this.isStringLikeCppType(cppType)) {
770
+ return { format: "%s", arg: rendered, estimatedLength: 32 };
771
+ }
772
+ if (cppType === "bool") {
773
+ return { format: "%s", arg: `(${rendered} ? "true" : "false")`, estimatedLength: 5 };
774
+ }
721
775
  return { format: "%d", arg: rendered, estimatedLength: 12 };
722
776
  }
723
777
  case "method-call":
@@ -878,10 +932,27 @@ export class ExpressionRenderer {
878
932
  * (safer than a wrong cast).
879
933
  */
880
934
  renderValueForTarget(expr, targetType, knownVariableTypes, exprTransformer) {
881
- const rendered = this.render(expr, exprTransformer, knownVariableTypes);
882
935
  if (!targetType)
883
- return rendered;
936
+ return this.render(expr, exprTransformer, knownVariableTypes);
884
937
  const normalizedTarget = targetType.trim();
938
+ // Inject template args for constructor calls on pre-C++17 toolchains
939
+ // (e.g. AVR without CTAD). SafeVariable(0) → SafeVariable<int32_t>(0).
940
+ // Handles both `method-call` (when the constructor appears as a standalone
941
+ // initializer) and `call` (when it appears on the RHS of an assignment,
942
+ // which lowers as `T x = {}; x = T(args);`). The callee must match the
943
+ // template name and the target must be a `Name<...>` instantiation, so a
944
+ // regular function call assigned to a non-templated type never matches.
945
+ const tmplMatch = normalizedTarget.match(/^([A-Za-z_]\w*)<(.+)>/);
946
+ const exprAny = expr;
947
+ const isCtorCall = tmplMatch != null
948
+ && (exprAny.kind === "method-call" || exprAny.kind === "call")
949
+ && exprAny.callee === tmplMatch[1]
950
+ && exprAny.callee != null && !exprAny.callee.includes("<");
951
+ if (isCtorCall && exprAny.args) {
952
+ const argsText = exprAny.args.map(a => this.render(a, exprTransformer, knownVariableTypes)).join(", ");
953
+ return `${tmplMatch[1]}<${tmplMatch[2]}>(${argsText})`;
954
+ }
955
+ const rendered = this.render(expr, exprTransformer, knownVariableTypes);
885
956
  // Never cast into string-like storage — enums that lower to const char*
886
957
  // (string enums) already match, and a string target is never an enum boundary.
887
958
  if (this.isStringLikeCppType(normalizedTarget))
@@ -1350,6 +1421,8 @@ export class ExpressionRenderer {
1350
1421
  renderMethodCall(expr, exprTransformer) {
1351
1422
  const argsText = expr.args.map(a => this.render(a, exprTransformer)).join(", ");
1352
1423
  let callee = exprTransformer ? exprTransformer(expr.callee) : expr.callee;
1424
+ const rExpr = expr.receiverExpr;
1425
+ const mName = expr.methodName;
1353
1426
  // Convert . to :: for static or namespace method calls if the IR flagged them.
1354
1427
  if (expr.isStatic || expr.isNamespace) {
1355
1428
  callee = callee.replace(/\./g, "::");
@@ -1380,6 +1453,18 @@ export class ExpressionRenderer {
1380
1453
  return callee;
1381
1454
  }
1382
1455
  callee = this.escapeFinalMemberName(callee);
1456
+ // Structured-receiver path: when the method-call IR carries a receiverExpr
1457
+ // (the safe.read().ok().fail() chain builder sets this), render the receiver
1458
+ // via the full renderer so nested lambda args survive (the flat callee string
1459
+ // would have destroyed them as /* __lambda__ */ placeholders). The methodName
1460
+ // is escaped against reserved names (e.g. div -> div_).
1461
+ const receiverExpr = expr.receiverExpr;
1462
+ const methodName = expr.methodName;
1463
+ if (receiverExpr !== undefined && methodName !== undefined) {
1464
+ const receiverText = this.render(receiverExpr, exprTransformer);
1465
+ const escapedMethod = escapeCppKeyword(methodName, this.strategy.reservedNames());
1466
+ return `${receiverText}.${escapedMethod}(${argsText})`;
1467
+ }
1383
1468
  return `${callee}(${argsText})`;
1384
1469
  }
1385
1470
  escapeFinalMemberName(callee) {
@@ -1,13 +1,26 @@
1
1
  // ---------------------------------------------------------------------------
2
2
  // Op routing — dispatch a HALOpIR to the right strategy resolver.
3
3
  //
4
- // display.* ops go to resolveDisplayOp (graphics-specific); everything else
5
- // goes to resolveHALOperation (the existing generic seam). Keeps the consumer
6
- // sites (expression-renderer, statement-renderer, render-expr) DRY.
4
+ // display.* ops go to resolveDisplayOp (graphics-specific).
5
+ // safety.* ops go to the safety hook's resolveSafetyOp (when @typecad/safety
6
+ // is loaded) — NEVER to a per-target strategy, because the safety
7
+ // package is MCU-agnostic by construction.
8
+ // everything else goes to resolveHALOperation (the existing generic seam).
9
+ // Keeps the consumer sites (expression-renderer, statement-renderer,
10
+ // render-expr) DRY.
7
11
  // ---------------------------------------------------------------------------
12
+ import { getSafetyHook } from "../safety-hook.js";
8
13
  export function routeHALOp(op, strategy) {
9
- if (typeof op.operation === "string" && op.operation.startsWith("display.")) {
10
- return strategy.resolveDisplayOp?.(op);
14
+ if (typeof op.operation === "string") {
15
+ if (op.operation.startsWith("display.")) {
16
+ return strategy.resolveDisplayOp?.(op);
17
+ }
18
+ if (op.operation.startsWith("safety.")) {
19
+ // Safety ops are resolved by the @typecad/safety package via hook.
20
+ // Returns undefined if the package is not loaded (caller emits an
21
+ // unhandled-op warning).
22
+ return getSafetyHook()?.resolveSafetyOp?.(op);
23
+ }
11
24
  }
12
25
  return strategy.resolveHALOperation?.(op);
13
26
  }
@@ -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
@@ -235,11 +236,14 @@ export function inferSnprintfArg(expr, strategy, scopeState, renderExpression, p
235
236
  return { format: "%d", arg: renderExpression(expr), estimatedLength: 12, preludeLines: [] };
236
237
  }
237
238
  }
238
- const arduinoIntFunctions = new Set([
239
- "digitalRead", "analogRead", "pulseIn", "pulseInLong",
240
- "Wire_available", "Serial_available",
239
+ const halIntFunctions = new Set([
240
+ ...(strategy?.halCallNames?.() ?? new Set()),
241
+ // pulseIn/pulseInLong and the Wire_available/Serial_available forms
242
+ // are framework-specific suffix conventions; frameworks may add them
243
+ // to halCallNames. Kept as a fallback here until all frameworks do.
244
+ "pulseIn", "pulseInLong", "Wire_available", "Serial_available",
241
245
  ]);
242
- if (arduinoIntFunctions.has(funcName)) {
246
+ if (halIntFunctions.has(funcName)) {
243
247
  return { format: "%d", arg: renderExpression(expr), estimatedLength: 12, preludeLines: [] };
244
248
  }
245
249
  }
@@ -369,6 +373,13 @@ export function inferSnprintfArg(expr, strategy, scopeState, renderExpression, p
369
373
  if (rendered.startsWith('"') || stringVarNames?.has(rendered)) {
370
374
  return { format: "%s", arg: rendered, estimatedLength: 32, preludeLines: [] };
371
375
  }
376
+ const cppType = cppTypeForHalOp(expr.operation.operation);
377
+ if (cppType && strategy.isStringLikeType(cppType)) {
378
+ return { format: "%s", arg: rendered, estimatedLength: 32, preludeLines: [] };
379
+ }
380
+ if (cppType === "bool") {
381
+ return { format: "%s", arg: `(${rendered} ? "true" : "false")`, estimatedLength: 5, preludeLines: [] };
382
+ }
372
383
  return { format: "%d", arg: rendered, estimatedLength: 12, preludeLines: [] };
373
384
  }
374
385
  default:
@@ -63,6 +63,14 @@ interface StatementRendererContext {
63
63
  * list. Optional so ad-hoc/test constructions still work.
64
64
  */
65
65
  diagnostics?: Diagnostic[];
66
+ /**
67
+ * Optional compliance context for A3-9-1 (fixed-width integer default).
68
+ * When provided and A3-9-1 is enforced, defaultNumericType() returns a
69
+ * fixed-width type instead of the legacy 'int'.
70
+ */
71
+ compliance?: {
72
+ isBanned(ruleId: string): boolean;
73
+ };
66
74
  }
67
75
  /**
68
76
  * Renders StatementIR nodes to C++ code strings.
@@ -83,6 +91,8 @@ export declare class StatementRenderer {
83
91
  private readonly interfaceFieldTypes;
84
92
  /** Shared emit-time diagnostics sink (see StatementRendererContext.diagnostics). */
85
93
  private readonly _diagnostics;
94
+ /** Compliance context for A3-9-1 fixed-width integer default. */
95
+ private readonly _compliance?;
86
96
  constructor(context: StatementRendererContext);
87
97
  /**
88
98
  * Gets the underlying expression renderer for direct use.
@@ -116,6 +116,7 @@ export class StatementRenderer {
116
116
  this.namespaceNames = context.namespaceNames ?? new Set();
117
117
  this.interfaceFieldTypes = context.interfaceFieldTypes ?? new Map();
118
118
  this._diagnostics = context.diagnostics ?? [];
119
+ this._compliance = context.compliance;
119
120
  // Create expression renderer with shared context
120
121
  this.expressionRenderer = new ExpressionRenderer({
121
122
  strategy: context.strategy,
@@ -324,7 +325,7 @@ export class StatementRenderer {
324
325
  this.expressionRenderer.pushPrelude([
325
326
  `const char* ${idxVar}_keys[] = { ${keysArr} };`,
326
327
  ]);
327
- return `for (${this.strategy.defaultNumericType()} ${idxVar} = 0; ${idxVar} < ${statement.keys.length}; ${idxVar}++)`;
328
+ return `for (${this.strategy.defaultNumericType(this._compliance)} ${idxVar} = 0; ${idxVar} < ${statement.keys.length}; ${idxVar}++)`;
328
329
  }
329
330
  const varDecl = statement.variable;
330
331
  if (varDecl.kind === "var_decl") {
@@ -485,6 +486,28 @@ export class StatementRenderer {
485
486
  const rawStmt = statement.callee.slice('__RAW_STMT__'.length);
486
487
  return forHeader ? rawStmt : `${rawStmt.endsWith(';') ? rawStmt : rawStmt + ';'}`;
487
488
  }
489
+ // Handle expression statements whose IR must render at EMIT time (not the
490
+ // build-time renderExprAsText). The safe.read().ok().fail() chain uses this:
491
+ // its lambda args can only render via the emit-time renderLambda (which
492
+ // produces [&](){...}), so the structured chain IR is carried as args[0]
493
+ // and rendered here via the expression renderer.
494
+ if (statement.callee === '__EXPR_STMT__' && statement.args.length >= 1) {
495
+ const exprText = this.expressionRenderer.render(statement.args[0], undefined, knownVariableTypes);
496
+ return forHeader ? exprText : `${exprText};`;
497
+ }
498
+ // Awaited network markers (__WIFI_WAIT__/__HTTP_WAIT__) reaching the
499
+ // plain renderer means the await sits outside an async state machine
500
+ // (top-level await, or a position the state-machine splitter doesn't
501
+ // support). Fall back to the blocking form of the op they carry.
502
+ if ((statement.callee === "__WIFI_WAIT__" || statement.callee === "__HTTP_WAIT__" || statement.callee === "__BLE_WAIT__" || statement.callee === "__HAL_WAIT__")
503
+ && statement.args[0]?.kind === "hal-expr") {
504
+ const resolved = routeHALOp(statement.args[0].operation, this.strategy);
505
+ const code = resolved?.code ?? (resolved?.expression ? `${resolved.expression};` : undefined);
506
+ if (code) {
507
+ return forHeader ? code.replace(/;$/, "") : code;
508
+ }
509
+ return `/* unhandled awaited hal-op: ${statement.args[0].operation.operation} */`;
510
+ }
488
511
  // Handle emit() — compile-time C++ injection
489
512
  if (statement.callee === "__EMIT__") {
490
513
  const rawText = statement.args.map(arg => this.renderEmitArg(arg)).join("");
@@ -645,13 +668,13 @@ export class StatementRenderer {
645
668
  // that don't support std::vector → emit as a plain C-style array.
646
669
  // Mutable arrays (.push/.pop/.indexOf) are rewritten to StaticArray<int> in
647
670
  // the IR builder (via mutableArrayVars) and never reach this branch.
648
- const elementType = parsedElementString(rawType) ?? this.strategy.defaultNumericType();
671
+ const elementType = parsedElementString(rawType) ?? this.strategy.defaultNumericType(this._compliance);
649
672
  return forHeader
650
673
  ? `${elementType} ${safeArrName}[] = { ${elements} }`
651
674
  : `${elementType} ${safeArrName}[] = { ${elements} };`;
652
675
  }
653
676
  // Use "int" for "auto" element type since C arrays need explicit types
654
- const arrayType = statement.initializer.elementType === "auto" ? this.strategy.defaultNumericType() : statement.initializer.elementType;
677
+ const arrayType = statement.initializer.elementType === "auto" ? this.strategy.defaultNumericType(this._compliance) : statement.initializer.elementType;
655
678
  return forHeader
656
679
  ? `${arrayType} ${safeArrName}[] = { ${elements} }`
657
680
  : `${arrayType} ${safeArrName}[] = { ${elements} };`;
@@ -726,7 +749,7 @@ export class StatementRenderer {
726
749
  }
727
750
  // Handle spread array initializers
728
751
  if (statement.initializer.kind === "spread_array") {
729
- let arrayType = statement.initializer.elementType === "auto" ? this.strategy.defaultNumericType() : statement.initializer.elementType;
752
+ let arrayType = statement.initializer.elementType === "auto" ? this.strategy.defaultNumericType(this._compliance) : statement.initializer.elementType;
730
753
  const spreadName = this.expressionRenderer.render(statement.initializer.spreadExpr, calleeTransformer, knownVariableTypes);
731
754
  if (statement.initializer.elementType === "auto" && knownVariableTypes && statement.initializer.spreadExpr.kind === "identifier") {
732
755
  const srcInfo = knownVariableTypes.get(statement.initializer.spreadExpr.value);
@@ -879,11 +902,17 @@ export class StatementRenderer {
879
902
  if (this.stringEnumNames.has(typeName)) {
880
903
  return this.strategy.normalizeCppType("const char*");
881
904
  }
905
+ // A3-9-1: under autosar, substitute the platform's default numeric type
906
+ // for the legacy "int" spelling. The IR layer hardcodes "int" for number
907
+ // literals, so we catch it here at the renderer boundary.
908
+ if (typeName === "int" && this._compliance?.isBanned("A3-9-1")) {
909
+ return this.strategy.defaultNumericType(this._compliance);
910
+ }
882
911
  return this.strategy.normalizeCppType(typeName);
883
912
  }
884
913
  inferFieldType(value, parentName, fieldName) {
885
914
  return inferObjectFieldType(value, this.pointerVarTypes, this.knownFunctionReturnTypes, undefined, undefined, undefined, // largeEnumNames - would need to pass through
886
- parentName, fieldName, this.strategy.defaultNumericType(), (o, n) => this.strategy.resolvePinType?.(o, n));
915
+ parentName, fieldName, this.strategy.defaultNumericType(this._compliance), (o, n) => this.strategy.resolvePinType?.(o, n));
887
916
  }
888
917
  /**
889
918
  * Transform console.log/error/warn calls based on target platform.