c-next 0.1.61 → 0.1.63
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 +86 -63
- package/grammar/CNext.g4 +3 -17
- package/package.json +1 -1
- package/src/cli/serve/ServeCommand.ts +57 -45
- package/src/lib/__tests__/parseCHeader.mocked.test.ts +145 -0
- package/src/transpiler/Transpiler.ts +603 -613
- package/src/transpiler/__tests__/DualCodePaths.test.ts +5 -1
- package/src/transpiler/__tests__/Transpiler.coverage.test.ts +2 -99
- package/src/transpiler/__tests__/Transpiler.test.ts +3 -26
- package/src/transpiler/data/IncludeTreeWalker.ts +1 -1
- package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +23 -52
- package/src/transpiler/logic/parser/grammar/CNext.interp +1 -3
- package/src/transpiler/logic/parser/grammar/CNextListener.ts +0 -22
- package/src/transpiler/logic/parser/grammar/CNextParser.ts +665 -1084
- package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +0 -14
- package/src/transpiler/logic/symbols/CppSymbolCollector.ts +67 -43
- package/src/transpiler/logic/symbols/cnext/collectors/StructCollector.ts +156 -70
- package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +31 -6
- package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +43 -11
- package/src/transpiler/output/codegen/CodeGenState.ts +811 -0
- package/src/transpiler/output/codegen/CodeGenerator.ts +1410 -2587
- package/src/transpiler/output/codegen/TypeResolver.ts +193 -149
- package/src/transpiler/output/codegen/TypeValidator.ts +148 -370
- package/src/transpiler/output/codegen/__tests__/CodeGenState.test.ts +446 -0
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +2082 -52
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +1 -1
- package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +435 -196
- package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +51 -67
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +495 -471
- package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +227 -66
- package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +55 -58
- package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +288 -275
- package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +101 -144
- package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +195 -133
- package/src/transpiler/output/codegen/assignment/AssignmentContextBuilder.ts +24 -74
- package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +3 -0
- package/src/transpiler/output/codegen/assignment/IAssignmentContext.ts +3 -0
- package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +290 -320
- package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +42 -0
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +76 -2
- package/src/transpiler/output/codegen/generators/GeneratorRegistry.ts +12 -0
- package/src/transpiler/output/codegen/generators/IOrchestrator.ts +5 -1
- package/src/transpiler/output/codegen/generators/__tests__/GeneratorRegistry.test.ts +28 -1
- package/src/transpiler/output/codegen/generators/declarationGenerators/ArrayDimensionUtils.ts +67 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/RegisterGenerator.ts +11 -24
- package/src/transpiler/output/codegen/generators/declarationGenerators/RegisterMacroGenerator.ts +64 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +137 -61
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopedRegisterGenerator.ts +18 -27
- package/src/transpiler/output/codegen/generators/declarationGenerators/StructGenerator.ts +100 -23
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ArrayDimensionUtils.test.ts +125 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +157 -4
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +5 -1
- package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +1 -17
- package/src/transpiler/output/codegen/generators/support/HelperGenerator.ts +23 -22
- package/src/transpiler/output/codegen/helpers/ArrayAccessHelper.ts +129 -0
- package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +54 -61
- package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +40 -44
- package/src/transpiler/output/codegen/helpers/AssignmentTargetExtractor.ts +17 -45
- package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +83 -78
- package/src/transpiler/output/codegen/helpers/CppModeHelper.ts +22 -30
- package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +108 -50
- package/src/transpiler/output/codegen/helpers/FloatBitHelper.ts +16 -31
- package/src/transpiler/output/codegen/helpers/MemberSeparatorResolver.ts +10 -3
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +103 -96
- package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +44 -0
- package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +9 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ArrayAccessHelper.test.ts +479 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +58 -103
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +97 -40
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +223 -128
- package/src/transpiler/output/codegen/helpers/__tests__/CppModeHelper.test.ts +68 -41
- package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +198 -47
- package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +39 -37
- package/src/transpiler/output/codegen/helpers/__tests__/MemberSeparatorResolver.test.ts +1 -0
- package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +191 -453
- package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +201 -0
- package/src/transpiler/output/codegen/helpers/__tests__/TypeGenerationHelper.test.ts +50 -0
- package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +229 -0
- package/src/transpiler/output/codegen/resolution/ScopeResolver.ts +60 -0
- package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +177 -0
- package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +336 -0
- package/src/transpiler/output/codegen/resolution/__tests__/SizeofResolver.test.ts +201 -0
- package/src/transpiler/output/codegen/types/IArrayAccessDeps.ts +23 -0
- package/src/transpiler/output/codegen/types/IArrayAccessInfo.ts +26 -0
- package/src/transpiler/output/codegen/types/IMemberSeparatorDeps.ts +7 -0
- package/src/transpiler/output/codegen/utils/CodegenParserUtils.ts +98 -0
- package/src/transpiler/output/codegen/utils/ExpressionUnwrapper.ts +22 -22
- package/src/transpiler/output/codegen/utils/__tests__/CodegenParserUtils.test.ts +228 -0
- package/src/transpiler/types/IFileResult.ts +0 -4
- package/src/transpiler/types/IPipelineFile.ts +27 -0
- package/src/transpiler/types/IPipelineInput.ts +23 -0
- package/src/transpiler/types/TranspilerState.ts +1 -1
- package/src/utils/FormatUtils.ts +28 -2
- package/src/utils/MapUtils.ts +25 -0
- package/src/utils/PostfixAnalysisUtils.ts +48 -0
- package/src/utils/__tests__/FormatUtils.test.ts +42 -0
- package/src/utils/__tests__/MapUtils.test.ts +85 -0
- package/src/utils/constants/OperatorMappings.ts +19 -0
- package/src/transpiler/logic/StandaloneContextBuilder.ts +0 -150
- package/src/transpiler/logic/__tests__/StandaloneContextBuilder.test.ts +0 -647
- package/src/transpiler/output/codegen/types/ITypeResolverDeps.ts +0 -23
- package/src/transpiler/output/codegen/types/ITypeValidatorDeps.ts +0 -53
- package/src/transpiler/types/ITranspileContext.ts +0 -49
- package/src/transpiler/types/ITranspileContribution.ts +0 -32
package/src/transpiler/output/codegen/generators/declarationGenerators/ScopedRegisterGenerator.ts
CHANGED
|
@@ -13,6 +13,7 @@ import IGeneratorInput from "../IGeneratorInput";
|
|
|
13
13
|
import IGeneratorState from "../IGeneratorState";
|
|
14
14
|
import IGeneratorOutput from "../IGeneratorOutput";
|
|
15
15
|
import IOrchestrator from "../IOrchestrator";
|
|
16
|
+
import generateRegisterMacros from "./RegisterMacroGenerator";
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Generate register macros with scope prefix.
|
|
@@ -28,35 +29,25 @@ const generateScopedRegister = (
|
|
|
28
29
|
const fullName = `${scopeName}_${name}`; // Teensy4_GPIO7
|
|
29
30
|
const baseAddress = orchestrator.generateExpression(node.expression());
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
// Generate individual #define for each register member with its offset
|
|
35
|
-
for (const member of node.registerMember()) {
|
|
36
|
-
const regName = member.IDENTIFIER().getText();
|
|
37
|
-
let regType = orchestrator.generateType(member.type());
|
|
38
|
-
const access = member.accessModifier().getText();
|
|
39
|
-
const offset = orchestrator.generateExpression(member.expression());
|
|
40
|
-
|
|
41
|
-
// Check if the type is a scoped bitmap (e.g., GPIO7Pins -> Teensy4_GPIO7Pins)
|
|
32
|
+
// Type resolver for scoped bitmaps (e.g., GPIO7Pins -> Teensy4_GPIO7Pins)
|
|
33
|
+
const resolveType = (regType: string): string | undefined => {
|
|
42
34
|
const scopedTypeName = `${scopeName}_${regType}`;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
// Determine qualifiers based on access mode
|
|
48
|
-
let cast = `volatile ${regType}*`;
|
|
49
|
-
if (access === "ro") {
|
|
50
|
-
cast = `volatile ${regType} const *`;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Generate: #define Teensy4_GPIO7_DR (*(volatile uint32_t*)(0x42004000 + 0x00))
|
|
54
|
-
lines.push(
|
|
55
|
-
`#define ${fullName}_${regName} (*(${cast})(${baseAddress} + ${offset}))`,
|
|
56
|
-
);
|
|
57
|
-
}
|
|
35
|
+
return input.symbols?.knownBitmaps.has(scopedTypeName)
|
|
36
|
+
? scopedTypeName
|
|
37
|
+
: undefined;
|
|
38
|
+
};
|
|
58
39
|
|
|
59
|
-
lines
|
|
40
|
+
const lines: string[] = [
|
|
41
|
+
`/* Register: ${fullName} @ ${baseAddress} */`,
|
|
42
|
+
...generateRegisterMacros(
|
|
43
|
+
node.registerMember(),
|
|
44
|
+
fullName,
|
|
45
|
+
baseAddress,
|
|
46
|
+
orchestrator,
|
|
47
|
+
resolveType,
|
|
48
|
+
),
|
|
49
|
+
"",
|
|
50
|
+
];
|
|
60
51
|
|
|
61
52
|
return {
|
|
62
53
|
code: lines.join("\n"),
|
|
@@ -21,6 +21,86 @@ import IGeneratorOutput from "../IGeneratorOutput";
|
|
|
21
21
|
import IOrchestrator from "../IOrchestrator";
|
|
22
22
|
import TGeneratorFn from "../TGeneratorFn";
|
|
23
23
|
import TGeneratorEffect from "../TGeneratorEffect";
|
|
24
|
+
import ICodeGenSymbols from "../../../../types/ICodeGenSymbols";
|
|
25
|
+
import ArrayDimensionUtils from "./ArrayDimensionUtils";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Generate a callback field declaration for a struct.
|
|
29
|
+
*/
|
|
30
|
+
function generateCallbackField(
|
|
31
|
+
fieldName: string,
|
|
32
|
+
callbackInfo: { typedefName: string },
|
|
33
|
+
isArray: boolean,
|
|
34
|
+
arrayDims: Parser.ArrayDimensionContext[],
|
|
35
|
+
orchestrator: IOrchestrator,
|
|
36
|
+
): string {
|
|
37
|
+
if (isArray) {
|
|
38
|
+
const dims = orchestrator.generateArrayDimensions(arrayDims);
|
|
39
|
+
return ` ${callbackInfo.typedefName} ${fieldName}${dims};`;
|
|
40
|
+
}
|
|
41
|
+
return ` ${callbackInfo.typedefName} ${fieldName};`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Generate a regular (non-callback) field declaration for a struct.
|
|
46
|
+
*/
|
|
47
|
+
function generateRegularField(
|
|
48
|
+
fieldName: string,
|
|
49
|
+
structName: string,
|
|
50
|
+
member: Parser.StructMemberContext,
|
|
51
|
+
isArray: boolean,
|
|
52
|
+
arrayDims: Parser.ArrayDimensionContext[],
|
|
53
|
+
input: IGeneratorInput,
|
|
54
|
+
orchestrator: IOrchestrator,
|
|
55
|
+
): string {
|
|
56
|
+
const type = orchestrator.generateType(member.type());
|
|
57
|
+
|
|
58
|
+
// Check for arrayType syntax: u8[16] data -> member.type().arrayType()
|
|
59
|
+
// Use optional chaining for mock compatibility in tests
|
|
60
|
+
const arrayTypeCtx = member.type().arrayType?.() ?? null;
|
|
61
|
+
const arrayTypeDimStr = ArrayDimensionUtils.generateArrayTypeDimension(
|
|
62
|
+
arrayTypeCtx,
|
|
63
|
+
orchestrator,
|
|
64
|
+
);
|
|
65
|
+
const hasArrayTypeSyntax = arrayTypeCtx !== null;
|
|
66
|
+
|
|
67
|
+
// Check if we have tracked dimensions for this field (includes string capacity for string arrays)
|
|
68
|
+
const fieldDims = getTrackedFieldDimensions(
|
|
69
|
+
input.symbols,
|
|
70
|
+
structName,
|
|
71
|
+
fieldName,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (fieldDims !== undefined) {
|
|
75
|
+
// Use tracked dimensions (includes string capacity for string arrays)
|
|
76
|
+
const dimsStr = fieldDims.map((d) => `[${d}]`).join("");
|
|
77
|
+
return ` ${type} ${fieldName}${dimsStr};`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (hasArrayTypeSyntax || isArray) {
|
|
81
|
+
// Combine arrayType dimension (if any) with arrayDimension dimensions
|
|
82
|
+
const dims = orchestrator.generateArrayDimensions(arrayDims);
|
|
83
|
+
return ` ${type} ${fieldName}${arrayTypeDimStr}${dims};`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return ` ${type} ${fieldName};`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get tracked field dimensions from symbols if available.
|
|
91
|
+
*/
|
|
92
|
+
function getTrackedFieldDimensions(
|
|
93
|
+
symbols: ICodeGenSymbols | null,
|
|
94
|
+
structName: string,
|
|
95
|
+
fieldName: string,
|
|
96
|
+
): readonly number[] | undefined {
|
|
97
|
+
if (!symbols) {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
const trackedDimensions = symbols.structFieldDimensions.get(structName);
|
|
101
|
+
const fieldDims = trackedDimensions?.get(fieldName);
|
|
102
|
+
return fieldDims && fieldDims.length > 0 ? fieldDims : undefined;
|
|
103
|
+
}
|
|
24
104
|
|
|
25
105
|
/**
|
|
26
106
|
* Generate a C typedef struct from a C-Next struct declaration.
|
|
@@ -64,31 +144,28 @@ const generateStruct: TGeneratorFn<Parser.StructDeclarationContext> = (
|
|
|
64
144
|
typeName,
|
|
65
145
|
});
|
|
66
146
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
147
|
+
lines.push(
|
|
148
|
+
generateCallbackField(
|
|
149
|
+
fieldName,
|
|
150
|
+
callbackInfo,
|
|
151
|
+
isArray,
|
|
152
|
+
arrayDims,
|
|
153
|
+
orchestrator,
|
|
154
|
+
),
|
|
155
|
+
);
|
|
73
156
|
} else {
|
|
74
157
|
// Regular field handling
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
// Fall back to AST dimensions for non-string arrays
|
|
87
|
-
const dims = orchestrator.generateArrayDimensions(arrayDims);
|
|
88
|
-
lines.push(` ${type} ${fieldName}${dims};`);
|
|
89
|
-
} else {
|
|
90
|
-
lines.push(` ${type} ${fieldName};`);
|
|
91
|
-
}
|
|
158
|
+
lines.push(
|
|
159
|
+
generateRegularField(
|
|
160
|
+
fieldName,
|
|
161
|
+
name,
|
|
162
|
+
member,
|
|
163
|
+
isArray,
|
|
164
|
+
arrayDims,
|
|
165
|
+
input,
|
|
166
|
+
orchestrator,
|
|
167
|
+
),
|
|
168
|
+
);
|
|
92
169
|
}
|
|
93
170
|
}
|
|
94
171
|
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import ArrayDimensionUtils from "../ArrayDimensionUtils";
|
|
3
|
+
import IOrchestrator from "../../IOrchestrator";
|
|
4
|
+
|
|
5
|
+
describe("ArrayDimensionUtils", () => {
|
|
6
|
+
const createMockOrchestrator = (
|
|
7
|
+
constValue?: number,
|
|
8
|
+
exprText = "EXPR",
|
|
9
|
+
): IOrchestrator => {
|
|
10
|
+
return {
|
|
11
|
+
tryEvaluateConstant: vi.fn().mockReturnValue(constValue),
|
|
12
|
+
generateExpression: vi.fn().mockReturnValue(exprText),
|
|
13
|
+
} as unknown as IOrchestrator;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
describe("generateArrayTypeDimension", () => {
|
|
17
|
+
it("returns empty string for null context", () => {
|
|
18
|
+
const orchestrator = createMockOrchestrator();
|
|
19
|
+
const result = ArrayDimensionUtils.generateArrayTypeDimension(
|
|
20
|
+
null,
|
|
21
|
+
orchestrator,
|
|
22
|
+
);
|
|
23
|
+
expect(result).toBe("");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("returns [] for context with no expression", () => {
|
|
27
|
+
const orchestrator = createMockOrchestrator();
|
|
28
|
+
const ctx = {
|
|
29
|
+
expression: () => null,
|
|
30
|
+
};
|
|
31
|
+
const result = ArrayDimensionUtils.generateArrayTypeDimension(
|
|
32
|
+
ctx as never,
|
|
33
|
+
orchestrator,
|
|
34
|
+
);
|
|
35
|
+
expect(result).toBe("[]");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("returns constant dimension when evaluable", () => {
|
|
39
|
+
const orchestrator = createMockOrchestrator(16);
|
|
40
|
+
const ctx = {
|
|
41
|
+
expression: () => ({ getText: () => "16" }),
|
|
42
|
+
};
|
|
43
|
+
const result = ArrayDimensionUtils.generateArrayTypeDimension(
|
|
44
|
+
ctx as never,
|
|
45
|
+
orchestrator,
|
|
46
|
+
);
|
|
47
|
+
expect(result).toBe("[16]");
|
|
48
|
+
expect(orchestrator.tryEvaluateConstant).toHaveBeenCalled();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("falls back to expression generation for non-constant", () => {
|
|
52
|
+
const orchestrator = createMockOrchestrator(undefined, "BUFFER_SIZE");
|
|
53
|
+
const mockExpr = { getText: () => "BUFFER_SIZE" };
|
|
54
|
+
const ctx = {
|
|
55
|
+
expression: () => mockExpr,
|
|
56
|
+
};
|
|
57
|
+
const result = ArrayDimensionUtils.generateArrayTypeDimension(
|
|
58
|
+
ctx as never,
|
|
59
|
+
orchestrator,
|
|
60
|
+
);
|
|
61
|
+
expect(result).toBe("[BUFFER_SIZE]");
|
|
62
|
+
expect(orchestrator.generateExpression).toHaveBeenCalledWith(mockExpr);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe("generateStringCapacityDim", () => {
|
|
67
|
+
it("returns empty string for non-string type", () => {
|
|
68
|
+
const ctx = {
|
|
69
|
+
stringType: () => null,
|
|
70
|
+
};
|
|
71
|
+
const result = ArrayDimensionUtils.generateStringCapacityDim(
|
|
72
|
+
ctx as never,
|
|
73
|
+
);
|
|
74
|
+
expect(result).toBe("");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("returns empty string for string without capacity", () => {
|
|
78
|
+
const ctx = {
|
|
79
|
+
stringType: () => ({
|
|
80
|
+
INTEGER_LITERAL: () => null,
|
|
81
|
+
}),
|
|
82
|
+
};
|
|
83
|
+
const result = ArrayDimensionUtils.generateStringCapacityDim(
|
|
84
|
+
ctx as never,
|
|
85
|
+
);
|
|
86
|
+
expect(result).toBe("");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("returns capacity + 1 for string with capacity", () => {
|
|
90
|
+
const ctx = {
|
|
91
|
+
stringType: () => ({
|
|
92
|
+
INTEGER_LITERAL: () => ({
|
|
93
|
+
getText: () => "32",
|
|
94
|
+
}),
|
|
95
|
+
}),
|
|
96
|
+
};
|
|
97
|
+
const result = ArrayDimensionUtils.generateStringCapacityDim(
|
|
98
|
+
ctx as never,
|
|
99
|
+
);
|
|
100
|
+
expect(result).toBe("[33]");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("handles various capacity values", () => {
|
|
104
|
+
const testCases = [
|
|
105
|
+
{ input: "8", expected: "[9]" },
|
|
106
|
+
{ input: "64", expected: "[65]" },
|
|
107
|
+
{ input: "255", expected: "[256]" },
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
for (const { input, expected } of testCases) {
|
|
111
|
+
const ctx = {
|
|
112
|
+
stringType: () => ({
|
|
113
|
+
INTEGER_LITERAL: () => ({
|
|
114
|
+
getText: () => input,
|
|
115
|
+
}),
|
|
116
|
+
}),
|
|
117
|
+
};
|
|
118
|
+
const result = ArrayDimensionUtils.generateStringCapacityDim(
|
|
119
|
+
ctx as never,
|
|
120
|
+
);
|
|
121
|
+
expect(result).toBe(expected);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
});
|
|
@@ -42,10 +42,35 @@ function createMockConstModifier(
|
|
|
42
42
|
return isConst ? ({} as Parser.ConstModifierContext) : null;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Create a mock array type context.
|
|
47
|
+
*/
|
|
48
|
+
function createMockArrayType(sizeExpr?: string | null) {
|
|
49
|
+
if (sizeExpr === null) {
|
|
50
|
+
// Empty brackets - no expression
|
|
51
|
+
return {
|
|
52
|
+
expression: () => null,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
expression: () =>
|
|
57
|
+
sizeExpr
|
|
58
|
+
? {
|
|
59
|
+
getText: () => sizeExpr,
|
|
60
|
+
__mockValue: sizeExpr,
|
|
61
|
+
}
|
|
62
|
+
: null,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
45
66
|
/**
|
|
46
67
|
* Create a mock type context.
|
|
47
68
|
*/
|
|
48
|
-
function createMockType(
|
|
69
|
+
function createMockType(
|
|
70
|
+
typeName: string,
|
|
71
|
+
hasStringType = false,
|
|
72
|
+
arrayTypeSize?: string | null,
|
|
73
|
+
) {
|
|
49
74
|
return {
|
|
50
75
|
getText: () => typeName,
|
|
51
76
|
stringType: () =>
|
|
@@ -54,6 +79,8 @@ function createMockType(typeName: string, hasStringType = false) {
|
|
|
54
79
|
INTEGER_LITERAL: () => ({ getText: () => "32" }),
|
|
55
80
|
}
|
|
56
81
|
: null,
|
|
82
|
+
arrayType: () =>
|
|
83
|
+
arrayTypeSize !== undefined ? createMockArrayType(arrayTypeSize) : null,
|
|
57
84
|
};
|
|
58
85
|
}
|
|
59
86
|
|
|
@@ -99,10 +126,16 @@ function createMockVariableDecl(options: {
|
|
|
99
126
|
hasStringType?: boolean;
|
|
100
127
|
constructorArgs?: string[];
|
|
101
128
|
startLine?: number;
|
|
129
|
+
arrayTypeSize?: string | null; // C-Next style: u16[8] name
|
|
102
130
|
}) {
|
|
103
131
|
return {
|
|
104
132
|
IDENTIFIER: () => ({ getText: () => options.name }),
|
|
105
|
-
type: () =>
|
|
133
|
+
type: () =>
|
|
134
|
+
createMockType(
|
|
135
|
+
options.type,
|
|
136
|
+
options.hasStringType,
|
|
137
|
+
options.arrayTypeSize,
|
|
138
|
+
),
|
|
106
139
|
constModifier: () => createMockConstModifier(options.isConst ?? false),
|
|
107
140
|
expression: () =>
|
|
108
141
|
options.initialValue ? createMockExpression(options.initialValue) : null,
|
|
@@ -207,10 +240,11 @@ function createMockStructMember(
|
|
|
207
240
|
type: string,
|
|
208
241
|
arrayDims?: string[],
|
|
209
242
|
hasStringType = false,
|
|
243
|
+
arrayTypeSize?: string | null,
|
|
210
244
|
) {
|
|
211
245
|
return {
|
|
212
246
|
IDENTIFIER: () => ({ getText: () => name }),
|
|
213
|
-
type: () => createMockType(type, hasStringType),
|
|
247
|
+
type: () => createMockType(type, hasStringType, arrayTypeSize),
|
|
214
248
|
arrayDimension: () => (arrayDims ?? []).map(createMockArrayDimension),
|
|
215
249
|
};
|
|
216
250
|
}
|
|
@@ -225,13 +259,20 @@ function createMockStructDecl(
|
|
|
225
259
|
type: string;
|
|
226
260
|
arrayDims?: string[];
|
|
227
261
|
hasStringType?: boolean;
|
|
262
|
+
arrayTypeSize?: string | null;
|
|
228
263
|
}>,
|
|
229
264
|
) {
|
|
230
265
|
return {
|
|
231
266
|
IDENTIFIER: () => ({ getText: () => name }),
|
|
232
267
|
structMember: () =>
|
|
233
268
|
members.map((m) =>
|
|
234
|
-
createMockStructMember(
|
|
269
|
+
createMockStructMember(
|
|
270
|
+
m.name,
|
|
271
|
+
m.type,
|
|
272
|
+
m.arrayDims,
|
|
273
|
+
m.hasStringType,
|
|
274
|
+
m.arrayTypeSize,
|
|
275
|
+
),
|
|
235
276
|
),
|
|
236
277
|
};
|
|
237
278
|
}
|
|
@@ -579,6 +620,66 @@ describe("ScopeGenerator", () => {
|
|
|
579
620
|
expect(result.code).toContain("static uint8_t Serial_buffer[256] = {0};");
|
|
580
621
|
});
|
|
581
622
|
|
|
623
|
+
it("generates C-Next style array variable with constant size", () => {
|
|
624
|
+
const varDecl = createMockVariableDecl({
|
|
625
|
+
name: "data",
|
|
626
|
+
type: "u16[8]",
|
|
627
|
+
arrayTypeSize: "8",
|
|
628
|
+
});
|
|
629
|
+
const member = createMockScopeMember({ variableDecl: varDecl });
|
|
630
|
+
const ctx = createMockScopeContext("Buffer", [member]);
|
|
631
|
+
const input = createMockInput();
|
|
632
|
+
const state = createMockState();
|
|
633
|
+
const orchestrator = createMockOrchestrator({
|
|
634
|
+
...createMockOrchestrator(),
|
|
635
|
+
tryEvaluateConstant: vi.fn(() => 8),
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
const result = generateScope(ctx, input, state, orchestrator);
|
|
639
|
+
|
|
640
|
+
expect(result.code).toContain("static u16[8] Buffer_data[8] = {0};");
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
it("generates C-Next style array variable with non-constant expression (fallback)", () => {
|
|
644
|
+
const varDecl = createMockVariableDecl({
|
|
645
|
+
name: "items",
|
|
646
|
+
type: "u16[BUFFER_SIZE]",
|
|
647
|
+
arrayTypeSize: "BUFFER_SIZE",
|
|
648
|
+
});
|
|
649
|
+
const member = createMockScopeMember({ variableDecl: varDecl });
|
|
650
|
+
const ctx = createMockScopeContext("Storage", [member]);
|
|
651
|
+
const input = createMockInput();
|
|
652
|
+
const state = createMockState();
|
|
653
|
+
const orchestrator = createMockOrchestrator({
|
|
654
|
+
...createMockOrchestrator(),
|
|
655
|
+
tryEvaluateConstant: vi.fn(() => undefined), // Can't resolve macro
|
|
656
|
+
generateExpression: vi.fn(() => "BUFFER_SIZE"),
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
const result = generateScope(ctx, input, state, orchestrator);
|
|
660
|
+
|
|
661
|
+
expect(result.code).toContain(
|
|
662
|
+
"static u16[BUFFER_SIZE] Storage_items[BUFFER_SIZE] = {0};",
|
|
663
|
+
);
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
it("generates C-Next style array variable with no size (empty brackets)", () => {
|
|
667
|
+
const varDecl = createMockVariableDecl({
|
|
668
|
+
name: "flexible",
|
|
669
|
+
type: "u8[]",
|
|
670
|
+
arrayTypeSize: null, // No size expression
|
|
671
|
+
});
|
|
672
|
+
const member = createMockScopeMember({ variableDecl: varDecl });
|
|
673
|
+
const ctx = createMockScopeContext("Dynamic", [member]);
|
|
674
|
+
const input = createMockInput();
|
|
675
|
+
const state = createMockState();
|
|
676
|
+
const orchestrator = createMockOrchestrator();
|
|
677
|
+
|
|
678
|
+
const result = generateScope(ctx, input, state, orchestrator);
|
|
679
|
+
|
|
680
|
+
expect(result.code).toContain("static u8[] Dynamic_flexible[] = {0};");
|
|
681
|
+
});
|
|
682
|
+
|
|
582
683
|
it("generates string variable with capacity dimension (ADR-045)", () => {
|
|
583
684
|
const varDecl = createMockVariableDecl({
|
|
584
685
|
name: "message",
|
|
@@ -1036,6 +1137,58 @@ describe("ScopeGenerator", () => {
|
|
|
1036
1137
|
expect(result.code).toContain("uint8_t data[256];");
|
|
1037
1138
|
});
|
|
1038
1139
|
|
|
1140
|
+
it("generates struct field with C-Next array type constant size", () => {
|
|
1141
|
+
const structDecl = createMockStructDecl("Container", [
|
|
1142
|
+
{ name: "items", type: "u16[4]", arrayTypeSize: "4" },
|
|
1143
|
+
]);
|
|
1144
|
+
const member = createMockScopeMember({ structDecl: structDecl });
|
|
1145
|
+
const ctx = createMockScopeContext("Data", [member]);
|
|
1146
|
+
const input = createMockInput();
|
|
1147
|
+
const state = createMockState();
|
|
1148
|
+
const orchestrator = createMockOrchestrator({
|
|
1149
|
+
...createMockOrchestrator(),
|
|
1150
|
+
tryEvaluateConstant: vi.fn(() => 4),
|
|
1151
|
+
});
|
|
1152
|
+
|
|
1153
|
+
const result = generateScope(ctx, input, state, orchestrator);
|
|
1154
|
+
|
|
1155
|
+
expect(result.code).toContain("u16[4] items[4];");
|
|
1156
|
+
});
|
|
1157
|
+
|
|
1158
|
+
it("generates struct field with C-Next array type non-constant (fallback)", () => {
|
|
1159
|
+
const structDecl = createMockStructDecl("FlexContainer", [
|
|
1160
|
+
{ name: "buffer", type: "u8[MAX_SIZE]", arrayTypeSize: "MAX_SIZE" },
|
|
1161
|
+
]);
|
|
1162
|
+
const member = createMockScopeMember({ structDecl: structDecl });
|
|
1163
|
+
const ctx = createMockScopeContext("Flex", [member]);
|
|
1164
|
+
const input = createMockInput();
|
|
1165
|
+
const state = createMockState();
|
|
1166
|
+
const orchestrator = createMockOrchestrator({
|
|
1167
|
+
...createMockOrchestrator(),
|
|
1168
|
+
tryEvaluateConstant: vi.fn(() => undefined), // Can't resolve macro
|
|
1169
|
+
generateExpression: vi.fn(() => "MAX_SIZE"),
|
|
1170
|
+
});
|
|
1171
|
+
|
|
1172
|
+
const result = generateScope(ctx, input, state, orchestrator);
|
|
1173
|
+
|
|
1174
|
+
expect(result.code).toContain("u8[MAX_SIZE] buffer[MAX_SIZE];");
|
|
1175
|
+
});
|
|
1176
|
+
|
|
1177
|
+
it("generates struct field with C-Next array type no size (empty brackets)", () => {
|
|
1178
|
+
const structDecl = createMockStructDecl("DynamicContainer", [
|
|
1179
|
+
{ name: "data", type: "u8[]", arrayTypeSize: null },
|
|
1180
|
+
]);
|
|
1181
|
+
const member = createMockScopeMember({ structDecl: structDecl });
|
|
1182
|
+
const ctx = createMockScopeContext("Dyn", [member]);
|
|
1183
|
+
const input = createMockInput();
|
|
1184
|
+
const state = createMockState();
|
|
1185
|
+
const orchestrator = createMockOrchestrator();
|
|
1186
|
+
|
|
1187
|
+
const result = generateScope(ctx, input, state, orchestrator);
|
|
1188
|
+
|
|
1189
|
+
expect(result.code).toContain("u8[] data[];");
|
|
1190
|
+
});
|
|
1191
|
+
|
|
1039
1192
|
it("generates struct field with string capacity", () => {
|
|
1040
1193
|
const structDecl = createMockStructDecl("Message", [
|
|
1041
1194
|
{ name: "text", type: "string<64>", hasStringType: true },
|
|
@@ -931,7 +931,11 @@ const tryKnownScopeAccess = (
|
|
|
931
931
|
state.currentScope,
|
|
932
932
|
);
|
|
933
933
|
}
|
|
934
|
-
orchestrator.validateCrossScopeVisibility(
|
|
934
|
+
orchestrator.validateCrossScopeVisibility(
|
|
935
|
+
ctx.result,
|
|
936
|
+
ctx.memberName,
|
|
937
|
+
ctx.isGlobalAccess,
|
|
938
|
+
);
|
|
935
939
|
|
|
936
940
|
const output = initializeMemberOutput(ctx);
|
|
937
941
|
output.result = `${ctx.result}${orchestrator.getScopeSeparator(ctx.isCppAccessChain)}${ctx.memberName}`;
|
|
@@ -25,23 +25,7 @@ import IGeneratorState from "../IGeneratorState";
|
|
|
25
25
|
import IOrchestrator from "../IOrchestrator";
|
|
26
26
|
import VariableModifierBuilder from "../../helpers/VariableModifierBuilder";
|
|
27
27
|
import ExpressionUtils from "../../../../../utils/ExpressionUtils";
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Maps C-Next assignment operators to C assignment operators
|
|
31
|
-
*/
|
|
32
|
-
const ASSIGNMENT_OPERATOR_MAP: Record<string, string> = {
|
|
33
|
-
"<-": "=",
|
|
34
|
-
"+<-": "+=",
|
|
35
|
-
"-<-": "-=",
|
|
36
|
-
"*<-": "*=",
|
|
37
|
-
"/<-": "/=",
|
|
38
|
-
"%<-": "%=",
|
|
39
|
-
"&<-": "&=",
|
|
40
|
-
"|<-": "|=",
|
|
41
|
-
"^<-": "^=",
|
|
42
|
-
"<<<-": "<<=",
|
|
43
|
-
">><-": ">>=",
|
|
44
|
-
};
|
|
28
|
+
import ASSIGNMENT_OPERATOR_MAP from "../../../../../utils/constants/OperatorMappings";
|
|
45
29
|
|
|
46
30
|
/**
|
|
47
31
|
* Issue #477: Check if a simple identifier is an unqualified enum member.
|
|
@@ -11,6 +11,27 @@
|
|
|
11
11
|
import TYPE_MAP from "../../types/TYPE_MAP";
|
|
12
12
|
import OverflowHelperTemplates from "./OverflowHelperTemplates";
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Generate a safe arithmetic helper function (div or mod).
|
|
16
|
+
* Extracted to eliminate duplication between safe_div and safe_mod generation.
|
|
17
|
+
*/
|
|
18
|
+
const generateSafeArithmeticHelper = (
|
|
19
|
+
opName: string,
|
|
20
|
+
opSymbol: string,
|
|
21
|
+
cnxType: string,
|
|
22
|
+
cType: string,
|
|
23
|
+
): string[] => [
|
|
24
|
+
`static inline bool cnx_safe_${opName}_${cnxType}(${cType}* output, ${cType} numerator, ${cType} divisor, ${cType} defaultValue) {`,
|
|
25
|
+
` if (divisor == 0) {`,
|
|
26
|
+
` *output = defaultValue;`,
|
|
27
|
+
` return true; // Error occurred`,
|
|
28
|
+
` }`,
|
|
29
|
+
` *output = numerator ${opSymbol} divisor;`,
|
|
30
|
+
` return false; // Success`,
|
|
31
|
+
`}`,
|
|
32
|
+
"",
|
|
33
|
+
];
|
|
34
|
+
|
|
14
35
|
/**
|
|
15
36
|
* Generate all needed overflow helper functions
|
|
16
37
|
* ADR-044: Overflow helper functions with clamping or panic behavior
|
|
@@ -90,32 +111,12 @@ const generateSafeDivHelpers = (
|
|
|
90
111
|
|
|
91
112
|
// Generate safe_div helper if needed
|
|
92
113
|
if (needsDiv) {
|
|
93
|
-
lines.push(
|
|
94
|
-
`static inline bool cnx_safe_div_${cnxType}(${cType}* output, ${cType} numerator, ${cType} divisor, ${cType} defaultValue) {`,
|
|
95
|
-
` if (divisor == 0) {`,
|
|
96
|
-
` *output = defaultValue;`,
|
|
97
|
-
` return true; // Error occurred`,
|
|
98
|
-
` }`,
|
|
99
|
-
` *output = numerator / divisor;`,
|
|
100
|
-
` return false; // Success`,
|
|
101
|
-
`}`,
|
|
102
|
-
"",
|
|
103
|
-
);
|
|
114
|
+
lines.push(...generateSafeArithmeticHelper("div", "/", cnxType, cType));
|
|
104
115
|
}
|
|
105
116
|
|
|
106
117
|
// Generate safe_mod helper if needed
|
|
107
118
|
if (needsMod) {
|
|
108
|
-
lines.push(
|
|
109
|
-
`static inline bool cnx_safe_mod_${cnxType}(${cType}* output, ${cType} numerator, ${cType} divisor, ${cType} defaultValue) {`,
|
|
110
|
-
` if (divisor == 0) {`,
|
|
111
|
-
` *output = defaultValue;`,
|
|
112
|
-
` return true; // Error occurred`,
|
|
113
|
-
` }`,
|
|
114
|
-
` *output = numerator % divisor;`,
|
|
115
|
-
` return false; // Success`,
|
|
116
|
-
`}`,
|
|
117
|
-
"",
|
|
118
|
-
);
|
|
119
|
+
lines.push(...generateSafeArithmeticHelper("mod", "%", cnxType, cType));
|
|
119
120
|
}
|
|
120
121
|
}
|
|
121
122
|
|