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.
- package/README.md +59 -57
- package/dist/index.js +859 -198
- package/dist/index.js.map +4 -4
- package/package.json +3 -1
- package/src/cli/Runner.ts +1 -1
- package/src/cli/__tests__/Runner.test.ts +8 -8
- package/src/cli/serve/ServeCommand.ts +29 -9
- package/src/transpiler/Transpiler.ts +105 -200
- package/src/transpiler/__tests__/DualCodePaths.test.ts +117 -68
- package/src/transpiler/__tests__/Transpiler.coverage.test.ts +87 -51
- package/src/transpiler/__tests__/Transpiler.test.ts +150 -48
- package/src/transpiler/__tests__/determineProjectRoot.test.ts +2 -2
- package/src/transpiler/data/IncludeResolver.ts +11 -3
- package/src/transpiler/data/__tests__/IncludeResolver.test.ts +2 -2
- package/src/transpiler/logic/analysis/ArrayIndexTypeAnalyzer.ts +346 -0
- package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +281 -14
- package/src/transpiler/logic/analysis/__tests__/ArrayIndexTypeAnalyzer.test.ts +545 -0
- package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +375 -0
- package/src/transpiler/logic/analysis/runAnalyzers.ts +9 -2
- package/src/transpiler/logic/analysis/types/IArrayIndexTypeError.ts +15 -0
- package/src/transpiler/logic/symbols/TransitiveEnumCollector.ts +1 -1
- package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +18 -0
- package/src/transpiler/logic/symbols/c/index.ts +50 -1
- package/src/transpiler/output/codegen/CodeGenerator.ts +69 -15
- package/src/transpiler/output/codegen/TypeResolver.ts +1 -1
- package/src/transpiler/output/codegen/TypeValidator.ts +10 -7
- package/src/transpiler/output/codegen/__tests__/ExpressionWalker.test.ts +9 -3
- package/src/transpiler/output/codegen/__tests__/RequireInclude.test.ts +86 -23
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +3 -1
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +43 -29
- package/src/transpiler/output/codegen/generators/IOrchestrator.ts +5 -2
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +15 -3
- package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +1 -1
- package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +12 -3
- package/src/transpiler/output/codegen/generators/statements/__tests__/ControlFlowGenerator.test.ts +4 -4
- package/src/transpiler/output/codegen/helpers/FunctionContextManager.ts +64 -3
- package/src/transpiler/output/codegen/helpers/MemberSeparatorResolver.ts +28 -6
- package/src/transpiler/output/codegen/helpers/ParameterDereferenceResolver.ts +12 -0
- package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +30 -2
- package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +15 -7
- package/src/transpiler/output/codegen/helpers/StringOperationsHelper.ts +1 -1
- package/src/transpiler/output/codegen/helpers/TypedefParamParser.ts +220 -0
- package/src/transpiler/output/codegen/helpers/__tests__/FunctionContextManager.test.ts +5 -5
- package/src/transpiler/output/codegen/helpers/__tests__/MemberSeparatorResolver.test.ts +48 -36
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +37 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +63 -0
- package/src/transpiler/output/codegen/helpers/__tests__/TypedefParamParser.test.ts +209 -0
- package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +1 -1
- package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +1 -1
- package/src/transpiler/output/codegen/types/IParameterInput.ts +13 -0
- package/src/transpiler/output/codegen/types/ISeparatorContext.ts +7 -0
- package/src/transpiler/output/codegen/types/TParameterInfo.ts +12 -0
- package/src/transpiler/output/codegen/utils/CodegenParserUtils.ts +1 -1
- package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +5 -2
- package/src/transpiler/state/CodeGenState.ts +25 -0
- package/src/transpiler/types/IPipelineFile.ts +2 -2
- package/src/transpiler/types/IPipelineInput.ts +1 -1
- package/src/transpiler/types/TTranspileInput.ts +18 -0
- package/src/{transpiler/output/codegen/utils → utils}/ExpressionUnwrapper.ts +1 -1
- package/src/{transpiler/output/codegen/utils → utils}/__tests__/ExpressionUnwrapper.test.ts +2 -2
- 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 "
|
|
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
|
|
1046
|
+
* Validate condition is a boolean expression (ADR-027, Issue #884).
|
|
1046
1047
|
* Part of IOrchestrator interface (ADR-053 A3).
|
|
1047
1048
|
*/
|
|
1048
|
-
|
|
1049
|
-
|
|
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
|
|
1567
|
-
* for cross-file const inference
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
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 "
|
|
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
|
-
//
|
|
717
|
+
// Condition Boolean Validation (ADR-027, Issue #884)
|
|
718
718
|
// ========================================================================
|
|
719
719
|
|
|
720
|
-
static
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
89
|
-
|
|
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 =
|
|
100
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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("
|
|
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(() =>
|
|
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(() =>
|
|
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(() =>
|
|
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(() =>
|
|
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(() =>
|
|
1902
|
-
"
|
|
1903
|
-
);
|
|
1904
|
-
expect(() =>
|
|
1905
|
-
"do-while
|
|
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(() =>
|
|
1918
|
-
"
|
|
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(() =>
|
|
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(() =>
|
|
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(() =>
|
|
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(() =>
|
|
2026
|
-
|
|
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(() =>
|
|
2043
|
-
"
|
|
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(() =>
|
|
2063
|
-
"
|
|
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(() =>
|
|
2086
|
-
"
|
|
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
|
|
212
|
-
|
|
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(
|