c-next 0.2.3 → 0.2.5

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 (61) hide show
  1. package/README.md +59 -57
  2. package/dist/index.js +859 -198
  3. package/dist/index.js.map +4 -4
  4. package/package.json +3 -1
  5. package/src/cli/Runner.ts +1 -1
  6. package/src/cli/__tests__/Runner.test.ts +8 -8
  7. package/src/cli/serve/ServeCommand.ts +29 -9
  8. package/src/transpiler/Transpiler.ts +105 -200
  9. package/src/transpiler/__tests__/DualCodePaths.test.ts +117 -68
  10. package/src/transpiler/__tests__/Transpiler.coverage.test.ts +87 -51
  11. package/src/transpiler/__tests__/Transpiler.test.ts +150 -48
  12. package/src/transpiler/__tests__/determineProjectRoot.test.ts +2 -2
  13. package/src/transpiler/data/IncludeResolver.ts +11 -3
  14. package/src/transpiler/data/__tests__/IncludeResolver.test.ts +2 -2
  15. package/src/transpiler/logic/analysis/ArrayIndexTypeAnalyzer.ts +346 -0
  16. package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +281 -14
  17. package/src/transpiler/logic/analysis/__tests__/ArrayIndexTypeAnalyzer.test.ts +545 -0
  18. package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +375 -0
  19. package/src/transpiler/logic/analysis/runAnalyzers.ts +9 -2
  20. package/src/transpiler/logic/analysis/types/IArrayIndexTypeError.ts +15 -0
  21. package/src/transpiler/logic/symbols/TransitiveEnumCollector.ts +1 -1
  22. package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +18 -0
  23. package/src/transpiler/logic/symbols/c/index.ts +50 -1
  24. package/src/transpiler/output/codegen/CodeGenerator.ts +69 -15
  25. package/src/transpiler/output/codegen/TypeResolver.ts +1 -1
  26. package/src/transpiler/output/codegen/TypeValidator.ts +10 -7
  27. package/src/transpiler/output/codegen/__tests__/ExpressionWalker.test.ts +9 -3
  28. package/src/transpiler/output/codegen/__tests__/RequireInclude.test.ts +86 -23
  29. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +3 -1
  30. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +43 -29
  31. package/src/transpiler/output/codegen/generators/IOrchestrator.ts +5 -2
  32. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +15 -3
  33. package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +1 -1
  34. package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +12 -3
  35. package/src/transpiler/output/codegen/generators/statements/__tests__/ControlFlowGenerator.test.ts +4 -4
  36. package/src/transpiler/output/codegen/helpers/FunctionContextManager.ts +64 -3
  37. package/src/transpiler/output/codegen/helpers/MemberSeparatorResolver.ts +28 -6
  38. package/src/transpiler/output/codegen/helpers/ParameterDereferenceResolver.ts +12 -0
  39. package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +30 -2
  40. package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +15 -7
  41. package/src/transpiler/output/codegen/helpers/StringOperationsHelper.ts +1 -1
  42. package/src/transpiler/output/codegen/helpers/TypedefParamParser.ts +220 -0
  43. package/src/transpiler/output/codegen/helpers/__tests__/FunctionContextManager.test.ts +5 -5
  44. package/src/transpiler/output/codegen/helpers/__tests__/MemberSeparatorResolver.test.ts +48 -36
  45. package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +37 -0
  46. package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +63 -0
  47. package/src/transpiler/output/codegen/helpers/__tests__/TypedefParamParser.test.ts +209 -0
  48. package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +1 -1
  49. package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +1 -1
  50. package/src/transpiler/output/codegen/types/IParameterInput.ts +13 -0
  51. package/src/transpiler/output/codegen/types/ISeparatorContext.ts +7 -0
  52. package/src/transpiler/output/codegen/types/TParameterInfo.ts +12 -0
  53. package/src/transpiler/output/codegen/utils/CodegenParserUtils.ts +1 -1
  54. package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +5 -2
  55. package/src/transpiler/state/CodeGenState.ts +25 -0
  56. package/src/transpiler/types/IPipelineFile.ts +2 -2
  57. package/src/transpiler/types/IPipelineInput.ts +1 -1
  58. package/src/transpiler/types/TTranspileInput.ts +18 -0
  59. package/src/{transpiler/output/codegen/utils → utils}/ExpressionUnwrapper.ts +1 -1
  60. package/src/{transpiler/output/codegen/utils → utils}/__tests__/ExpressionUnwrapper.test.ts +2 -2
  61. package/src/utils/constants/TypeConstants.ts +22 -0
@@ -118,7 +118,7 @@ import ISimpleIdentifierDeps from "./types/ISimpleIdentifierDeps";
118
118
  import IPostfixChainDeps from "./types/IPostfixChainDeps";
119
119
  import IPostfixOperation from "./types/IPostfixOperation";
120
120
  // Issue #707: Expression unwrapping utility for reducing duplication
121
- import ExpressionUnwrapper from "./utils/ExpressionUnwrapper";
121
+ import ExpressionUnwrapper from "../../../utils/ExpressionUnwrapper";
122
122
  // Stateless parser utilities extracted from CodeGenerator
123
123
  import CodegenParserUtils from "./utils/CodegenParserUtils";
124
124
  import IMemberSeparatorDeps from "./types/IMemberSeparatorDeps";
@@ -140,6 +140,7 @@ import PassByValueAnalyzer from "../../logic/analysis/PassByValueAnalyzer";
140
140
  // Unified parameter generation (Phase 1)
141
141
  import ParameterInputAdapter from "./helpers/ParameterInputAdapter";
142
142
  import ParameterSignatureBuilder from "./helpers/ParameterSignatureBuilder";
143
+ // Issue #895: Parse typedef signatures to determine pointer vs value params
143
144
  // Extracted resolvers that use CodeGenState
144
145
  import SizeofResolver from "./resolution/SizeofResolver";
145
146
  import EnumTypeResolver from "./resolution/EnumTypeResolver";
@@ -1042,11 +1043,14 @@ export default class CodeGenerator implements IOrchestrator {
1042
1043
  }
1043
1044
 
1044
1045
  /**
1045
- * Validate do-while condition.
1046
+ * Validate condition is a boolean expression (ADR-027, Issue #884).
1046
1047
  * Part of IOrchestrator interface (ADR-053 A3).
1047
1048
  */
1048
- validateDoWhileCondition(ctx: Parser.ExpressionContext): void {
1049
- TypeValidator.validateDoWhileCondition(ctx);
1049
+ validateConditionIsBoolean(
1050
+ ctx: Parser.ExpressionContext,
1051
+ conditionType: string,
1052
+ ): void {
1053
+ TypeValidator.validateConditionIsBoolean(ctx, conditionType);
1050
1054
  }
1051
1055
 
1052
1056
  /**
@@ -1238,7 +1242,7 @@ export default class CodeGenerator implements IOrchestrator {
1238
1242
  generateParameterList(ctx: Parser.ParameterListContext): string {
1239
1243
  return ctx
1240
1244
  .parameter()
1241
- .map((p) => this.generateParameter(p))
1245
+ .map((p, index) => this.generateParameter(p, index))
1242
1246
  .join(", ");
1243
1247
  }
1244
1248
 
@@ -1563,8 +1567,8 @@ export default class CodeGenerator implements IOrchestrator {
1563
1567
 
1564
1568
  /**
1565
1569
  * Issue #561: Analyze modifications in a parse tree without full code generation.
1566
- * Used by Pipeline.transpileSource() to collect modification info from includes
1567
- * for cross-file const inference (unified with Pipeline.run() behavior).
1570
+ * Used by the transpile() pipeline to collect modification info from includes
1571
+ * for cross-file const inference.
1568
1572
  *
1569
1573
  * Issue #565: Now accepts optional cross-file data for transitive propagation.
1570
1574
  * When a file calls a function from an included file that modifies its param,
@@ -3496,6 +3500,17 @@ export default class CodeGenerator implements IOrchestrator {
3496
3500
 
3497
3501
  // For C-Next/C structs, generate designated initializer
3498
3502
  const fieldInits = fields.map((f) => `.${f.fieldName} = ${f.value}`);
3503
+
3504
+ // Issue #882: In C++ mode, anonymous structs/unions must use plain brace init.
3505
+ // Compound literals like (struct { ... }){ ... } create incompatible types in C++
3506
+ // because each struct { ... } definition creates a distinct nominal type.
3507
+ if (
3508
+ CodeGenState.cppMode &&
3509
+ (typeName.startsWith("struct {") || typeName.startsWith("union {"))
3510
+ ) {
3511
+ return `{ ${fieldInits.join(", ")} }`;
3512
+ }
3513
+
3499
3514
  return `(${castType}){ ${fieldInits.join(", ")} }`;
3500
3515
  }
3501
3516
 
@@ -3670,7 +3685,10 @@ export default class CodeGenerator implements IOrchestrator {
3670
3685
  return typedef ? functionCode + typedef : functionCode;
3671
3686
  }
3672
3687
 
3673
- private generateParameter(ctx: Parser.ParameterContext): string {
3688
+ private generateParameter(
3689
+ ctx: Parser.ParameterContext,
3690
+ paramIndex?: number,
3691
+ ): string {
3674
3692
  const typeName = this.getTypeName(ctx.type());
3675
3693
  const name = ctx.IDENTIFIER().getText();
3676
3694
 
@@ -3682,9 +3700,22 @@ export default class CodeGenerator implements IOrchestrator {
3682
3700
 
3683
3701
  // Pre-compute CodeGenState-dependent values
3684
3702
  const isModified = this._isCurrentParameterModified(name);
3685
- const isPassByValue = this._isPassByValueType(typeName, name);
3703
+
3704
+ // Issue #895: For callback-compatible functions, determine pointer/value
3705
+ // from the typedef signature, not from normal C-Next pass-by-value rules
3706
+ const callbackInfo =
3707
+ paramIndex === undefined
3708
+ ? null
3709
+ : FunctionContextManager.getCallbackTypedefParamInfo(paramIndex);
3710
+ const isPassByValue = callbackInfo
3711
+ ? !callbackInfo.shouldBePointer
3712
+ : this._isPassByValueType(typeName, name);
3713
+ const isCallbackCompatible = callbackInfo !== null;
3686
3714
 
3687
3715
  // Build normalized input using adapter
3716
+ // Issue #895: Force pass-by-reference and const from typedef signature
3717
+ const forcePassByReference = callbackInfo?.shouldBePointer ?? false;
3718
+ const forceConst = callbackInfo?.shouldBeConst ?? false;
3688
3719
  const input = ParameterInputAdapter.fromAST(ctx, {
3689
3720
  getTypeName: (t) => this.getTypeName(t),
3690
3721
  generateType: (t) => this.generateType(t),
@@ -3694,6 +3725,9 @@ export default class CodeGenerator implements IOrchestrator {
3694
3725
  typeMap: TYPE_MAP,
3695
3726
  isModified,
3696
3727
  isPassByValue,
3728
+ isCallbackCompatible,
3729
+ forcePassByReference,
3730
+ forceConst,
3697
3731
  });
3698
3732
 
3699
3733
  // Use shared builder with C/C++ mode
@@ -3763,6 +3797,21 @@ export default class CodeGenerator implements IOrchestrator {
3763
3797
  return true;
3764
3798
  }
3765
3799
 
3800
+ // Callback-compatible functions: struct params become pass-by-value
3801
+ // to match C function pointer typedef signatures
3802
+ // NOTE: This assumes the C typedef expects pass-by-value structs.
3803
+ // Issue #895 describes cases where the typedef expects pointers instead.
3804
+ // A full fix requires parsing the typedef signature to determine which.
3805
+ if (
3806
+ CodeGenState.currentFunctionName &&
3807
+ CodeGenState.callbackCompatibleFunctions.has(
3808
+ CodeGenState.currentFunctionName,
3809
+ ) &&
3810
+ this.isKnownStruct(typeName)
3811
+ ) {
3812
+ return true;
3813
+ }
3814
+
3766
3815
  return false;
3767
3816
  }
3768
3817
 
@@ -4152,16 +4201,21 @@ export default class CodeGenerator implements IOrchestrator {
4152
4201
  const isStructParam = paramInfo?.isStruct ?? false;
4153
4202
  const isCppAccess = hasGlobal && this.isCppScopeSymbol(firstId);
4154
4203
  const separatorDeps = this._buildMemberSeparatorDeps();
4204
+ // Issue #895: Callback-compatible params need pointer semantics even in C++ mode
4205
+ const forcePointerSemantics = paramInfo?.forcePointerSemantics ?? false;
4155
4206
 
4156
4207
  const separatorCtx: ISeparatorContext =
4157
4208
  MemberSeparatorResolver.buildContext(
4158
- firstId,
4159
- hasGlobal,
4160
- hasThis,
4161
- CodeGenState.currentScope,
4162
- isStructParam,
4209
+ {
4210
+ firstId,
4211
+ hasGlobal,
4212
+ hasThis,
4213
+ currentScope: CodeGenState.currentScope,
4214
+ isStructParam,
4215
+ isCppAccess,
4216
+ forcePointerSemantics,
4217
+ },
4163
4218
  separatorDeps,
4164
- isCppAccess,
4165
4219
  );
4166
4220
 
4167
4221
  return {
@@ -10,7 +10,7 @@ import SIGNED_TYPES from "./types/SIGNED_TYPES";
10
10
  import UNSIGNED_TYPES from "./types/UNSIGNED_TYPES";
11
11
  import TYPE_WIDTH from "./types/TYPE_WIDTH";
12
12
  import TYPE_RANGES from "./types/TYPE_RANGES";
13
- import ExpressionUnwrapper from "./utils/ExpressionUnwrapper";
13
+ import ExpressionUnwrapper from "../../../utils/ExpressionUnwrapper";
14
14
 
15
15
  /**
16
16
  * Internal type info tracked through postfix suffix chains.
@@ -714,16 +714,19 @@ class TypeValidator {
714
714
  }
715
715
 
716
716
  // ========================================================================
717
- // Do-While Validation (ADR-027)
717
+ // Condition Boolean Validation (ADR-027, Issue #884)
718
718
  // ========================================================================
719
719
 
720
- static validateDoWhileCondition(ctx: Parser.ExpressionContext): void {
720
+ static validateConditionIsBoolean(
721
+ ctx: Parser.ExpressionContext,
722
+ conditionType: string,
723
+ ): void {
721
724
  const ternaryExpr = ctx.ternaryExpression();
722
725
  const orExprs = ternaryExpr.orExpression();
723
726
 
724
727
  if (orExprs.length !== 1) {
725
728
  throw new Error(
726
- `Error E0701: do-while condition must be a boolean expression, not a ternary (MISRA C:2012 Rule 14.4)`,
729
+ `Error E0701: ${conditionType} condition must be a boolean expression, not a ternary (MISRA C:2012 Rule 14.4)`,
727
730
  );
728
731
  }
729
732
 
@@ -737,7 +740,7 @@ class TypeValidator {
737
740
  const andExpr = orExpr.andExpression(0);
738
741
  if (!andExpr) {
739
742
  throw new Error(
740
- `Error E0701: do-while condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)`,
743
+ `Error E0701: ${conditionType} condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)`,
741
744
  );
742
745
  }
743
746
 
@@ -748,7 +751,7 @@ class TypeValidator {
748
751
  const equalityExpr = andExpr.equalityExpression(0);
749
752
  if (!equalityExpr) {
750
753
  throw new Error(
751
- `Error E0701: do-while condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)`,
754
+ `Error E0701: ${conditionType} condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)`,
752
755
  );
753
756
  }
754
757
 
@@ -759,7 +762,7 @@ class TypeValidator {
759
762
  const relationalExpr = equalityExpr.relationalExpression(0);
760
763
  if (!relationalExpr) {
761
764
  throw new Error(
762
- `Error E0701: do-while condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)`,
765
+ `Error E0701: ${conditionType} condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)`,
763
766
  );
764
767
  }
765
768
 
@@ -773,7 +776,7 @@ class TypeValidator {
773
776
  }
774
777
 
775
778
  throw new Error(
776
- `Error E0701: do-while condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)\n help: use explicit comparison: ${text} > 0 or ${text} != 0`,
779
+ `Error E0701: ${conditionType} condition must be a boolean expression (comparison or logical operation), not '${text}' (MISRA C:2012 Rule 14.4)\n help: use explicit comparison: ${text} > 0 or ${text} != 0`,
777
780
  );
778
781
  }
779
782
 
@@ -312,7 +312,9 @@ describe("ExpressionWalker - const inference integration", () => {
312
312
  }
313
313
  `;
314
314
  const transpiler = new Transpiler({ inputs: [] });
315
- const transpileResult = await transpiler.transpileSource(source);
315
+ const transpileResult = (
316
+ await transpiler.transpile({ kind: "source", source: source })
317
+ ).files[0];
316
318
  expect(transpileResult.success).toBe(true);
317
319
  // The caller function should NOT have const param because x is passed to modifyParam
318
320
  // which modifies its parameter
@@ -340,7 +342,9 @@ describe("ExpressionWalker - const inference integration", () => {
340
342
  }
341
343
  `;
342
344
  const transpiler = new Transpiler({ inputs: [] });
343
- const transpileResult = await transpiler.transpileSource(source);
345
+ const transpileResult = (
346
+ await transpiler.transpile({ kind: "source", source: source })
347
+ ).files[0];
344
348
  expect(transpileResult.success).toBe(true);
345
349
  // x should not be const because it's passed through a modifying call chain
346
350
  expect(transpileResult.code).not.toContain("void caller(const uint32_t x)");
@@ -367,7 +371,9 @@ describe("ExpressionWalker - const inference integration", () => {
367
371
  }
368
372
  `;
369
373
  const transpiler = new Transpiler({ inputs: [] });
370
- const transpileResult = await transpiler.transpileSource(source);
374
+ const transpileResult = (
375
+ await transpiler.transpile({ kind: "source", source: source })
376
+ ).files[0];
371
377
  expect(transpileResult.success).toBe(true);
372
378
  // x should not be const
373
379
  expect(transpileResult.code).not.toContain("void caller(const uint32_t x)");
@@ -20,7 +20,9 @@ describe("CodeGenerator requireInclude", () => {
20
20
  it("includes stdint.h for u8 type", async () => {
21
21
  const transpiler = new Transpiler({ inputs: [], noCache: true }, mockFs);
22
22
 
23
- const result = await transpiler.transpileSource("u8 value <- 0;");
23
+ const result = (
24
+ await transpiler.transpile({ kind: "source", source: "u8 value <- 0;" })
25
+ ).files[0];
24
26
 
25
27
  expect(result.success).toBe(true);
26
28
  expect(result.code).toContain("#include <stdint.h>");
@@ -29,7 +31,12 @@ describe("CodeGenerator requireInclude", () => {
29
31
  it("includes stdint.h for u16 type", async () => {
30
32
  const transpiler = new Transpiler({ inputs: [], noCache: true }, mockFs);
31
33
 
32
- const result = await transpiler.transpileSource("u16 value <- 0;");
34
+ const result = (
35
+ await transpiler.transpile({
36
+ kind: "source",
37
+ source: "u16 value <- 0;",
38
+ })
39
+ ).files[0];
33
40
 
34
41
  expect(result.success).toBe(true);
35
42
  expect(result.code).toContain("#include <stdint.h>");
@@ -38,7 +45,12 @@ describe("CodeGenerator requireInclude", () => {
38
45
  it("includes stdint.h for u32 type", async () => {
39
46
  const transpiler = new Transpiler({ inputs: [], noCache: true }, mockFs);
40
47
 
41
- const result = await transpiler.transpileSource("u32 value <- 0;");
48
+ const result = (
49
+ await transpiler.transpile({
50
+ kind: "source",
51
+ source: "u32 value <- 0;",
52
+ })
53
+ ).files[0];
42
54
 
43
55
  expect(result.success).toBe(true);
44
56
  expect(result.code).toContain("#include <stdint.h>");
@@ -47,7 +59,12 @@ describe("CodeGenerator requireInclude", () => {
47
59
  it("includes stdint.h for i32 type", async () => {
48
60
  const transpiler = new Transpiler({ inputs: [], noCache: true }, mockFs);
49
61
 
50
- const result = await transpiler.transpileSource("i32 value <- 0;");
62
+ const result = (
63
+ await transpiler.transpile({
64
+ kind: "source",
65
+ source: "i32 value <- 0;",
66
+ })
67
+ ).files[0];
51
68
 
52
69
  expect(result.success).toBe(true);
53
70
  expect(result.code).toContain("#include <stdint.h>");
@@ -56,14 +73,19 @@ describe("CodeGenerator requireInclude", () => {
56
73
  it("includes stdint.h for bitmap types", async () => {
57
74
  const transpiler = new Transpiler({ inputs: [], noCache: true }, mockFs);
58
75
 
59
- const result = await transpiler.transpileSource(`
76
+ const result = (
77
+ await transpiler.transpile({
78
+ kind: "source",
79
+ source: `
60
80
  bitmap8 Flags {
61
81
  enabled,
62
82
  active,
63
83
  reserved[6]
64
84
  }
65
85
  Flags f <- 0;
66
- `);
86
+ `,
87
+ })
88
+ ).files[0];
67
89
 
68
90
  expect(result.success).toBe(true);
69
91
  expect(result.code).toContain("#include <stdint.h>");
@@ -74,7 +96,12 @@ describe("CodeGenerator requireInclude", () => {
74
96
  it("includes stdbool.h for bool type", async () => {
75
97
  const transpiler = new Transpiler({ inputs: [], noCache: true }, mockFs);
76
98
 
77
- const result = await transpiler.transpileSource("bool flag <- false;");
99
+ const result = (
100
+ await transpiler.transpile({
101
+ kind: "source",
102
+ source: "bool flag <- false;",
103
+ })
104
+ ).files[0];
78
105
 
79
106
  expect(result.success).toBe(true);
80
107
  expect(result.code).toContain("#include <stdbool.h>");
@@ -85,9 +112,12 @@ describe("CodeGenerator requireInclude", () => {
85
112
  it("includes string.h for bounded string type", async () => {
86
113
  const transpiler = new Transpiler({ inputs: [], noCache: true }, mockFs);
87
114
 
88
- const result = await transpiler.transpileSource(
89
- 'string<32> name <- "test";',
90
- );
115
+ const result = (
116
+ await transpiler.transpile({
117
+ kind: "source",
118
+ source: 'string<32> name <- "test";',
119
+ })
120
+ ).files[0];
91
121
 
92
122
  expect(result.success).toBe(true);
93
123
  expect(result.code).toContain("#include <string.h>");
@@ -96,9 +126,12 @@ describe("CodeGenerator requireInclude", () => {
96
126
  it("includes string.h for const string inference", async () => {
97
127
  const transpiler = new Transpiler({ inputs: [], noCache: true }, mockFs);
98
128
 
99
- const result = await transpiler.transpileSource(
100
- 'const string message <- "hello";',
101
- );
129
+ const result = (
130
+ await transpiler.transpile({
131
+ kind: "source",
132
+ source: 'const string message <- "hello";',
133
+ })
134
+ ).files[0];
102
135
 
103
136
  expect(result.success).toBe(true);
104
137
  expect(result.code).toContain("#include <string.h>");
@@ -109,7 +142,12 @@ describe("CodeGenerator requireInclude", () => {
109
142
  it("generates ISR typedef for ISR type", async () => {
110
143
  const transpiler = new Transpiler({ inputs: [], noCache: true }, mockFs);
111
144
 
112
- const result = await transpiler.transpileSource("ISR handler <- null;");
145
+ const result = (
146
+ await transpiler.transpile({
147
+ kind: "source",
148
+ source: "ISR handler <- null;",
149
+ })
150
+ ).files[0];
113
151
 
114
152
  expect(result.success).toBe(true);
115
153
  expect(result.code).toContain("typedef void (*ISR)(void)");
@@ -120,13 +158,18 @@ describe("CodeGenerator requireInclude", () => {
120
158
  it("generates static assert for float bit indexing write", async () => {
121
159
  const transpiler = new Transpiler({ inputs: [], noCache: true }, mockFs);
122
160
 
123
- const result = await transpiler.transpileSource(`
161
+ const result = (
162
+ await transpiler.transpile({
163
+ kind: "source",
164
+ source: `
124
165
  f32 setByte(u8 b) {
125
166
  f32 value <- 0.0;
126
167
  value[0, 8] <- b;
127
168
  return value;
128
169
  }
129
- `);
170
+ `,
171
+ })
172
+ ).files[0];
130
173
 
131
174
  expect(result.success).toBe(true);
132
175
  expect(result.code).toContain("_Static_assert");
@@ -136,12 +179,17 @@ describe("CodeGenerator requireInclude", () => {
136
179
  it("generates static assert for float bit indexing read (no string.h)", async () => {
137
180
  const transpiler = new Transpiler({ inputs: [], noCache: true }, mockFs);
138
181
 
139
- const result = await transpiler.transpileSource(`
182
+ const result = (
183
+ await transpiler.transpile({
184
+ kind: "source",
185
+ source: `
140
186
  u8 getByte() {
141
187
  f32 value <- 1.0;
142
188
  return value[0, 8];
143
189
  }
144
- `);
190
+ `,
191
+ })
192
+ ).files[0];
145
193
 
146
194
  expect(result.success).toBe(true);
147
195
  expect(result.code).toContain("_Static_assert");
@@ -155,11 +203,16 @@ describe("CodeGenerator requireInclude", () => {
155
203
  it("includes limits.h for float-to-int clamp cast", async () => {
156
204
  const transpiler = new Transpiler({ inputs: [], noCache: true }, mockFs);
157
205
 
158
- const result = await transpiler.transpileSource(`
206
+ const result = (
207
+ await transpiler.transpile({
208
+ kind: "source",
209
+ source: `
159
210
  i32 convert(f32 value) {
160
211
  return (i32)value;
161
212
  }
162
- `);
213
+ `,
214
+ })
215
+ ).files[0];
163
216
 
164
217
  expect(result.success).toBe(true);
165
218
  expect(result.code).toContain("#include <limits.h>");
@@ -170,11 +223,16 @@ describe("CodeGenerator requireInclude", () => {
170
223
  it("includes multiple headers when needed", async () => {
171
224
  const transpiler = new Transpiler({ inputs: [], noCache: true }, mockFs);
172
225
 
173
- const result = await transpiler.transpileSource(`
226
+ const result = (
227
+ await transpiler.transpile({
228
+ kind: "source",
229
+ source: `
174
230
  bool check(u32 value, string<16> name) {
175
231
  return value > 0;
176
232
  }
177
- `);
233
+ `,
234
+ })
235
+ ).files[0];
178
236
 
179
237
  expect(result.success).toBe(true);
180
238
  expect(result.code).toContain("#include <stdint.h>");
@@ -185,7 +243,12 @@ describe("CodeGenerator requireInclude", () => {
185
243
  it("does not include unused headers", async () => {
186
244
  const transpiler = new Transpiler({ inputs: [], noCache: true }, mockFs);
187
245
 
188
- const result = await transpiler.transpileSource("void doNothing() { }");
246
+ const result = (
247
+ await transpiler.transpile({
248
+ kind: "source",
249
+ source: "void doNothing() { }",
250
+ })
251
+ ).files[0];
189
252
 
190
253
  expect(result.success).toBe(true);
191
254
  expect(result.code).not.toContain("#include <stdint.h>");
@@ -12,7 +12,9 @@ import Transpiler from "../../../Transpiler";
12
12
  */
13
13
  async function transpileSource(source: string): Promise<string> {
14
14
  const transpiler = new Transpiler({ inputs: [] });
15
- const result = await transpiler.transpileSource(source);
15
+ const result = (
16
+ await transpiler.transpile({ kind: "source", source: source })
17
+ ).files[0];
16
18
  if (result.errors && result.errors.length > 0) {
17
19
  throw new Error(
18
20
  `Transpile failed: ${result.errors.map((e) => e.message).join(", ")}`,
@@ -1820,7 +1820,7 @@ describe("TypeValidator", () => {
1820
1820
  // Tests - Do-While Validation (ADR-027)
1821
1821
  // ========================================================================
1822
1822
 
1823
- describe("validateDoWhileCondition", () => {
1823
+ describe("validateConditionIsBoolean", () => {
1824
1824
  function createFullDoWhileExpression(
1825
1825
  text: string,
1826
1826
  options?: {
@@ -1874,36 +1874,44 @@ describe("TypeValidator", () => {
1874
1874
  const ctx = createFullDoWhileExpression("x < 10", {
1875
1875
  hasRelational: true,
1876
1876
  });
1877
- expect(() => TypeValidator.validateDoWhileCondition(ctx)).not.toThrow();
1877
+ expect(() =>
1878
+ TypeValidator.validateConditionIsBoolean(ctx, "do-while"),
1879
+ ).not.toThrow();
1878
1880
  });
1879
1881
 
1880
1882
  it("allows conditions with equality operators", () => {
1881
1883
  setupState();
1882
1884
  const ctx = createFullDoWhileExpression("x = 0", { hasEquality: true });
1883
- expect(() => TypeValidator.validateDoWhileCondition(ctx)).not.toThrow();
1885
+ expect(() =>
1886
+ TypeValidator.validateConditionIsBoolean(ctx, "do-while"),
1887
+ ).not.toThrow();
1884
1888
  });
1885
1889
 
1886
1890
  it("allows conditions with && operator", () => {
1887
1891
  setupState();
1888
1892
  const ctx = createFullDoWhileExpression("a && b", { hasAnd: true });
1889
- expect(() => TypeValidator.validateDoWhileCondition(ctx)).not.toThrow();
1893
+ expect(() =>
1894
+ TypeValidator.validateConditionIsBoolean(ctx, "do-while"),
1895
+ ).not.toThrow();
1890
1896
  });
1891
1897
 
1892
1898
  it("allows conditions with || operator", () => {
1893
1899
  setupState();
1894
1900
  const ctx = createFullDoWhileExpression("a || b", { hasOr: true });
1895
- expect(() => TypeValidator.validateDoWhileCondition(ctx)).not.toThrow();
1901
+ expect(() =>
1902
+ TypeValidator.validateConditionIsBoolean(ctx, "do-while"),
1903
+ ).not.toThrow();
1896
1904
  });
1897
1905
 
1898
1906
  it("throws for bare value condition", () => {
1899
1907
  setupState();
1900
1908
  const ctx = createFullDoWhileExpression("count");
1901
- expect(() => TypeValidator.validateDoWhileCondition(ctx)).toThrow(
1902
- "E0701",
1903
- );
1904
- expect(() => TypeValidator.validateDoWhileCondition(ctx)).toThrow(
1905
- "do-while condition must be a boolean expression",
1906
- );
1909
+ expect(() =>
1910
+ TypeValidator.validateConditionIsBoolean(ctx, "do-while"),
1911
+ ).toThrow("E0701");
1912
+ expect(() =>
1913
+ TypeValidator.validateConditionIsBoolean(ctx, "do-while"),
1914
+ ).toThrow("do-while condition must be a boolean expression");
1907
1915
  });
1908
1916
 
1909
1917
  it("throws for ternary in do-while condition", () => {
@@ -1914,9 +1922,9 @@ describe("TypeValidator", () => {
1914
1922
  orExpression: () => [{}, {}], // Multiple orExpressions = ternary
1915
1923
  }),
1916
1924
  } as unknown as Parser.ExpressionContext;
1917
- expect(() => TypeValidator.validateDoWhileCondition(ctx)).toThrow(
1918
- "E0701",
1919
- );
1925
+ expect(() =>
1926
+ TypeValidator.validateConditionIsBoolean(ctx, "do-while"),
1927
+ ).toThrow("E0701");
1920
1928
  });
1921
1929
 
1922
1930
  it("allows boolean literals", () => {
@@ -1948,7 +1956,9 @@ describe("TypeValidator", () => {
1948
1956
  orExpression: antlrArray([orExpr]),
1949
1957
  }),
1950
1958
  } as unknown as Parser.ExpressionContext;
1951
- expect(() => TypeValidator.validateDoWhileCondition(ctx)).not.toThrow();
1959
+ expect(() =>
1960
+ TypeValidator.validateConditionIsBoolean(ctx, "do-while"),
1961
+ ).not.toThrow();
1952
1962
  });
1953
1963
 
1954
1964
  it("allows negation expressions", () => {
@@ -1979,7 +1989,9 @@ describe("TypeValidator", () => {
1979
1989
  orExpression: antlrArray([orExpr]),
1980
1990
  }),
1981
1991
  } as unknown as Parser.ExpressionContext;
1982
- expect(() => TypeValidator.validateDoWhileCondition(ctx)).not.toThrow();
1992
+ expect(() =>
1993
+ TypeValidator.validateConditionIsBoolean(ctx, "do-while"),
1994
+ ).not.toThrow();
1983
1995
  });
1984
1996
 
1985
1997
  it("allows bool type variables", () => {
@@ -2016,15 +2028,17 @@ describe("TypeValidator", () => {
2016
2028
  orExpression: antlrArray([orExpr]),
2017
2029
  }),
2018
2030
  } as unknown as Parser.ExpressionContext;
2019
- expect(() => TypeValidator.validateDoWhileCondition(ctx)).not.toThrow();
2031
+ expect(() =>
2032
+ TypeValidator.validateConditionIsBoolean(ctx, "do-while"),
2033
+ ).not.toThrow();
2020
2034
  });
2021
2035
 
2022
2036
  it("shows help message in error", () => {
2023
2037
  setupState();
2024
2038
  const ctx = createFullDoWhileExpression("count");
2025
- expect(() => TypeValidator.validateDoWhileCondition(ctx)).toThrow(
2026
- "help: use explicit comparison: count > 0 or count != 0",
2027
- );
2039
+ expect(() =>
2040
+ TypeValidator.validateConditionIsBoolean(ctx, "do-while"),
2041
+ ).toThrow("help: use explicit comparison: count > 0 or count != 0");
2028
2042
  });
2029
2043
 
2030
2044
  it("throws when no andExpression", () => {
@@ -2039,9 +2053,9 @@ describe("TypeValidator", () => {
2039
2053
  orExpression: antlrArray([orExpr]),
2040
2054
  }),
2041
2055
  } as unknown as Parser.ExpressionContext;
2042
- expect(() => TypeValidator.validateDoWhileCondition(ctx)).toThrow(
2043
- "E0701",
2044
- );
2056
+ expect(() =>
2057
+ TypeValidator.validateConditionIsBoolean(ctx, "do-while"),
2058
+ ).toThrow("E0701");
2045
2059
  });
2046
2060
 
2047
2061
  it("throws when no equalityExpression", () => {
@@ -2059,9 +2073,9 @@ describe("TypeValidator", () => {
2059
2073
  orExpression: antlrArray([orExpr]),
2060
2074
  }),
2061
2075
  } as unknown as Parser.ExpressionContext;
2062
- expect(() => TypeValidator.validateDoWhileCondition(ctx)).toThrow(
2063
- "E0701",
2064
- );
2076
+ expect(() =>
2077
+ TypeValidator.validateConditionIsBoolean(ctx, "do-while"),
2078
+ ).toThrow("E0701");
2065
2079
  });
2066
2080
 
2067
2081
  it("throws when no relationalExpression", () => {
@@ -2082,9 +2096,9 @@ describe("TypeValidator", () => {
2082
2096
  orExpression: antlrArray([orExpr]),
2083
2097
  }),
2084
2098
  } as unknown as Parser.ExpressionContext;
2085
- expect(() => TypeValidator.validateDoWhileCondition(ctx)).toThrow(
2086
- "E0701",
2087
- );
2099
+ expect(() =>
2100
+ TypeValidator.validateConditionIsBoolean(ctx, "do-while"),
2101
+ ).toThrow("E0701");
2088
2102
  });
2089
2103
  });
2090
2104
 
@@ -208,8 +208,11 @@ interface IOrchestrator {
208
208
  switchExpr: Parser.ExpressionContext,
209
209
  ): void;
210
210
 
211
- /** Validate do-while condition (ADR-027) */
212
- validateDoWhileCondition(ctx: Parser.ExpressionContext): void;
211
+ /** Validate condition is a boolean expression (ADR-027, Issue #884) */
212
+ validateConditionIsBoolean(
213
+ ctx: Parser.ExpressionContext,
214
+ conditionType: string,
215
+ ): void;
213
216
 
214
217
  /** Validate no function calls in condition (Issue #254, E0702) */
215
218
  validateConditionNoFunctionCall(