c-next 0.1.62 → 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/package.json +1 -1
- package/src/transpiler/Transpiler.ts +3 -2
- package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
- package/src/transpiler/__tests__/Transpiler.coverage.test.ts +1 -1
- package/src/transpiler/__tests__/Transpiler.test.ts +0 -23
- 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 +817 -1377
- 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 +326 -60
- 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 +39 -43
- package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +52 -55
- package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +122 -62
- package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +101 -144
- package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +143 -126
- package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +287 -320
- package/src/transpiler/output/codegen/generators/GeneratorRegistry.ts +12 -0
- 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/ScopeGenerator.ts +121 -51
- 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/support/HelperGenerator.ts +23 -22
- package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +54 -61
- package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +21 -30
- package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +56 -53
- 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/StringDeclHelper.ts +103 -96
- package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +9 -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__/StringDeclHelper.test.ts +191 -453
- 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/ITypeResolverDeps.ts +0 -23
- package/src/transpiler/output/codegen/types/ITypeValidatorDeps.ts +0 -53
|
@@ -102,6 +102,18 @@ export default class GeneratorRegistry {
|
|
|
102
102
|
this.expressions.set(kind, fn as TGeneratorFn<ParserRuleContext>);
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
// =========================================================================
|
|
106
|
+
// Deregistration Methods (for testing error paths)
|
|
107
|
+
// =========================================================================
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Unregister a declaration generator.
|
|
111
|
+
* Primarily used for testing error paths when generators are missing.
|
|
112
|
+
*/
|
|
113
|
+
unregisterDeclaration(kind: string): void {
|
|
114
|
+
this.declarations.delete(kind);
|
|
115
|
+
}
|
|
116
|
+
|
|
105
117
|
// =========================================================================
|
|
106
118
|
// Query Methods
|
|
107
119
|
// =========================================================================
|
|
@@ -9,7 +9,7 @@ const mockGenerator: TGeneratorFn<ParserRuleContext> = () => ({
|
|
|
9
9
|
effects: [],
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
describe("GeneratorRegistry", () => {
|
|
13
13
|
let registry: GeneratorRegistry;
|
|
14
14
|
|
|
15
15
|
beforeEach(() => {
|
|
@@ -48,4 +48,31 @@ export default describe("GeneratorRegistry", () => {
|
|
|
48
48
|
expect(registry.hasExpression("ternary")).toBe(true);
|
|
49
49
|
});
|
|
50
50
|
});
|
|
51
|
+
|
|
52
|
+
describe("unregisterDeclaration", () => {
|
|
53
|
+
it("removes a registered declaration", () => {
|
|
54
|
+
registry.registerDeclaration("struct", mockGenerator);
|
|
55
|
+
expect(registry.hasDeclaration("struct")).toBe(true);
|
|
56
|
+
|
|
57
|
+
registry.unregisterDeclaration("struct");
|
|
58
|
+
expect(registry.hasDeclaration("struct")).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("is a no-op for non-existent kind", () => {
|
|
62
|
+
expect(registry.hasDeclaration("unknown")).toBe(false);
|
|
63
|
+
registry.unregisterDeclaration("unknown");
|
|
64
|
+
expect(registry.hasDeclaration("unknown")).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe("getDeclaration", () => {
|
|
69
|
+
it("returns undefined for unregistered kind", () => {
|
|
70
|
+
expect(registry.getDeclaration("struct")).toBeUndefined();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("returns generator for registered kind", () => {
|
|
74
|
+
registry.registerDeclaration("struct", mockGenerator);
|
|
75
|
+
expect(registry.getDeclaration("struct")).toBe(mockGenerator);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
51
78
|
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ArrayDimensionUtils - Shared utilities for generating array dimension strings.
|
|
3
|
+
*
|
|
4
|
+
* Used by StructGenerator and ScopeGenerator for consistent array dimension handling.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as Parser from "../../../../logic/parser/grammar/CNextParser";
|
|
8
|
+
import IOrchestrator from "../IOrchestrator";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Generate array type dimension string from arrayType syntax (e.g., u8[16]).
|
|
12
|
+
* Evaluates constants when possible, falls back to expression generation.
|
|
13
|
+
*
|
|
14
|
+
* @param arrayTypeCtx - The arrayType context, or null if not present
|
|
15
|
+
* @param orchestrator - The orchestrator for constant evaluation and expression generation
|
|
16
|
+
* @returns Dimension string like "[16]", "[]", or "" if no arrayType
|
|
17
|
+
*/
|
|
18
|
+
function generateArrayTypeDimension(
|
|
19
|
+
arrayTypeCtx: Parser.ArrayTypeContext | null,
|
|
20
|
+
orchestrator: IOrchestrator,
|
|
21
|
+
): string {
|
|
22
|
+
if (arrayTypeCtx === null) {
|
|
23
|
+
return "";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const sizeExpr = arrayTypeCtx.expression();
|
|
27
|
+
if (!sizeExpr) {
|
|
28
|
+
return "[]";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const constValue = orchestrator.tryEvaluateConstant(sizeExpr);
|
|
32
|
+
if (constValue === undefined) {
|
|
33
|
+
// Fall back to expression generation for macros, enums, etc.
|
|
34
|
+
return `[${orchestrator.generateExpression(sizeExpr)}]`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return `[${constValue}]`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Generate string capacity dimension if applicable.
|
|
42
|
+
* Adds +1 for null terminator.
|
|
43
|
+
*
|
|
44
|
+
* @param typeCtx - The type context to check for string type
|
|
45
|
+
* @returns Dimension string like "[33]" for string<32>, or "" if not a string
|
|
46
|
+
*/
|
|
47
|
+
function generateStringCapacityDim(typeCtx: Parser.TypeContext): string {
|
|
48
|
+
const stringCtx = typeCtx.stringType();
|
|
49
|
+
if (!stringCtx) {
|
|
50
|
+
return "";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const intLiteral = stringCtx.INTEGER_LITERAL();
|
|
54
|
+
if (!intLiteral) {
|
|
55
|
+
return "";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const capacity = Number.parseInt(intLiteral.getText(), 10);
|
|
59
|
+
return `[${capacity + 1}]`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
class ArrayDimensionUtils {
|
|
63
|
+
static readonly generateArrayTypeDimension = generateArrayTypeDimension;
|
|
64
|
+
static readonly generateStringCapacityDim = generateStringCapacityDim;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export default ArrayDimensionUtils;
|
|
@@ -22,6 +22,22 @@ import IOrchestrator from "../IOrchestrator";
|
|
|
22
22
|
import TGeneratorFn from "../TGeneratorFn";
|
|
23
23
|
import generateScopedRegister from "./ScopedRegisterGenerator";
|
|
24
24
|
import BitmapCommentUtils from "./BitmapCommentUtils";
|
|
25
|
+
import ArrayDimensionUtils from "./ArrayDimensionUtils";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Generate initializer expression for a variable declaration.
|
|
29
|
+
*/
|
|
30
|
+
function generateInitializer(
|
|
31
|
+
varDecl: Parser.VariableDeclarationContext,
|
|
32
|
+
isArray: boolean,
|
|
33
|
+
orchestrator: IOrchestrator,
|
|
34
|
+
): string {
|
|
35
|
+
if (varDecl.expression()) {
|
|
36
|
+
return ` = ${orchestrator.generateExpression(varDecl.expression()!)}`;
|
|
37
|
+
}
|
|
38
|
+
// ADR-015: Zero initialization for uninitialized scope variables
|
|
39
|
+
return ` = ${orchestrator.getZeroInitializer(varDecl.type(), isArray)}`;
|
|
40
|
+
}
|
|
25
41
|
|
|
26
42
|
/**
|
|
27
43
|
* Extract scoped name from a declaration node.
|
|
@@ -81,30 +97,25 @@ function generateScopeVariable(
|
|
|
81
97
|
// Issue #375: Check for constructor syntax
|
|
82
98
|
const constructorArgList = varDecl.constructorArgumentList();
|
|
83
99
|
if (constructorArgList) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const prefix = isPrivate ? "static " : "";
|
|
88
|
-
|
|
89
|
-
// Validate and resolve constructor arguments
|
|
90
|
-
const argIdentifiers = constructorArgList.IDENTIFIER();
|
|
91
|
-
const line = varDecl.start?.line ?? 0;
|
|
92
|
-
const resolvedArgs = resolveConstructorArgs(
|
|
93
|
-
argIdentifiers,
|
|
100
|
+
return generateConstructorVariable(
|
|
101
|
+
varDecl,
|
|
102
|
+
varName,
|
|
94
103
|
scopeName,
|
|
95
|
-
|
|
104
|
+
isPrivate,
|
|
105
|
+
constructorArgList,
|
|
96
106
|
orchestrator,
|
|
97
107
|
);
|
|
98
|
-
|
|
99
|
-
return `${prefix}${type} ${fullName}(${resolvedArgs.join(", ")});`;
|
|
100
108
|
}
|
|
101
109
|
|
|
102
110
|
// Issue #282: Check if this is a const variable - const values should be inlined
|
|
103
111
|
const isConst = varDecl.constModifier() !== null;
|
|
104
112
|
|
|
105
113
|
// Issue #500: Check if array before skipping - arrays must be emitted
|
|
114
|
+
// Check both C-style arrayDimension and C-Next style arrayType
|
|
115
|
+
// Use optional chaining for mock compatibility in tests
|
|
106
116
|
const arrayDims = varDecl.arrayDimension();
|
|
107
|
-
const
|
|
117
|
+
const arrayTypeCtx = varDecl.type().arrayType?.() ?? null;
|
|
118
|
+
const isArray = arrayDims.length > 0 || arrayTypeCtx !== null;
|
|
108
119
|
|
|
109
120
|
// Issue #282: Private const variables should be inlined, not emitted at file scope
|
|
110
121
|
// Issue #500: EXCEPT arrays - arrays must be emitted as static const
|
|
@@ -113,6 +124,60 @@ function generateScopeVariable(
|
|
|
113
124
|
return null;
|
|
114
125
|
}
|
|
115
126
|
|
|
127
|
+
return generateRegularVariable(
|
|
128
|
+
varDecl,
|
|
129
|
+
varName,
|
|
130
|
+
scopeName,
|
|
131
|
+
isPrivate,
|
|
132
|
+
orchestrator,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Generate a constructor-style variable declaration.
|
|
138
|
+
*/
|
|
139
|
+
function generateConstructorVariable(
|
|
140
|
+
varDecl: Parser.VariableDeclarationContext,
|
|
141
|
+
varName: string,
|
|
142
|
+
scopeName: string,
|
|
143
|
+
isPrivate: boolean,
|
|
144
|
+
constructorArgList: Parser.ConstructorArgumentListContext,
|
|
145
|
+
orchestrator: IOrchestrator,
|
|
146
|
+
): string {
|
|
147
|
+
// ADR-016: All scope variables are emitted at file scope
|
|
148
|
+
const type = orchestrator.generateType(varDecl.type());
|
|
149
|
+
const fullName = `${scopeName}_${varName}`;
|
|
150
|
+
const prefix = isPrivate ? "static " : "";
|
|
151
|
+
|
|
152
|
+
// Validate and resolve constructor arguments
|
|
153
|
+
const argIdentifiers = constructorArgList.IDENTIFIER();
|
|
154
|
+
const line = varDecl.start?.line ?? 0;
|
|
155
|
+
const resolvedArgs = resolveConstructorArgs(
|
|
156
|
+
argIdentifiers,
|
|
157
|
+
scopeName,
|
|
158
|
+
line,
|
|
159
|
+
orchestrator,
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
return `${prefix}${type} ${fullName}(${resolvedArgs.join(", ")});`;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Generate a regular (non-constructor) variable declaration.
|
|
167
|
+
*/
|
|
168
|
+
function generateRegularVariable(
|
|
169
|
+
varDecl: Parser.VariableDeclarationContext,
|
|
170
|
+
varName: string,
|
|
171
|
+
scopeName: string,
|
|
172
|
+
isPrivate: boolean,
|
|
173
|
+
orchestrator: IOrchestrator,
|
|
174
|
+
): string {
|
|
175
|
+
// Derive array and const info from varDecl
|
|
176
|
+
const isConst = varDecl.constModifier() !== null;
|
|
177
|
+
const arrayDims = varDecl.arrayDimension();
|
|
178
|
+
const arrayTypeCtx = varDecl.type().arrayType?.() ?? null;
|
|
179
|
+
const isArray = arrayDims.length > 0 || arrayTypeCtx !== null;
|
|
180
|
+
|
|
116
181
|
// ADR-016: All scope variables are emitted at file scope (static-like persistence)
|
|
117
182
|
const type = orchestrator.generateType(varDecl.type());
|
|
118
183
|
const fullName = `${scopeName}_${varName}`;
|
|
@@ -120,26 +185,22 @@ function generateScopeVariable(
|
|
|
120
185
|
const constPrefix = isConst ? "const " : "";
|
|
121
186
|
const prefix = isPrivate ? "static " : "";
|
|
122
187
|
|
|
123
|
-
//
|
|
188
|
+
// Build declaration with all dimensions
|
|
124
189
|
let decl = `${prefix}${constPrefix}${type} ${fullName}`;
|
|
125
|
-
|
|
190
|
+
decl += ArrayDimensionUtils.generateArrayTypeDimension(
|
|
191
|
+
arrayTypeCtx,
|
|
192
|
+
orchestrator,
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
if (arrayDims.length > 0) {
|
|
196
|
+
// C-style or additional dimensions
|
|
126
197
|
decl += orchestrator.generateArrayDimensions(arrayDims);
|
|
127
198
|
}
|
|
199
|
+
|
|
128
200
|
// ADR-045: Add string capacity dimension for string arrays
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (intLiteral) {
|
|
133
|
-
const capacity = Number.parseInt(intLiteral.getText(), 10);
|
|
134
|
-
decl += `[${capacity + 1}]`;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
if (varDecl.expression()) {
|
|
138
|
-
decl += ` = ${orchestrator.generateExpression(varDecl.expression()!)}`;
|
|
139
|
-
} else {
|
|
140
|
-
// ADR-015: Zero initialization for uninitialized scope variables
|
|
141
|
-
decl += ` = ${orchestrator.getZeroInitializer(varDecl.type(), isArray)}`;
|
|
142
|
-
}
|
|
201
|
+
decl += ArrayDimensionUtils.generateStringCapacityDim(varDecl.type());
|
|
202
|
+
decl += generateInitializer(varDecl, isArray, orchestrator);
|
|
203
|
+
|
|
143
204
|
return decl + ";";
|
|
144
205
|
}
|
|
145
206
|
|
|
@@ -458,6 +519,35 @@ function generateScopedBitmapInline(
|
|
|
458
519
|
return lines.join("\n");
|
|
459
520
|
}
|
|
460
521
|
|
|
522
|
+
/**
|
|
523
|
+
* Generate a single struct field declaration for scoped struct.
|
|
524
|
+
*/
|
|
525
|
+
function generateScopedStructField(
|
|
526
|
+
member: Parser.StructMemberContext,
|
|
527
|
+
orchestrator: IOrchestrator,
|
|
528
|
+
): string {
|
|
529
|
+
const fieldName = member.IDENTIFIER().getText();
|
|
530
|
+
const fieldType = orchestrator.generateType(member.type());
|
|
531
|
+
|
|
532
|
+
// Handle C-Next style arrayType syntax (u16[8] arr)
|
|
533
|
+
const arrayTypeCtx = member.type().arrayType?.() ?? null;
|
|
534
|
+
let dimStr = ArrayDimensionUtils.generateArrayTypeDimension(
|
|
535
|
+
arrayTypeCtx,
|
|
536
|
+
orchestrator,
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
// Handle C-style array dimensions if present
|
|
540
|
+
const arrayDims = member.arrayDimension();
|
|
541
|
+
if (arrayDims.length > 0) {
|
|
542
|
+
dimStr += orchestrator.generateArrayDimensions(arrayDims);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// Handle string capacity for string fields
|
|
546
|
+
dimStr += ArrayDimensionUtils.generateStringCapacityDim(member.type());
|
|
547
|
+
|
|
548
|
+
return ` ${fieldType} ${fieldName}${dimStr};`;
|
|
549
|
+
}
|
|
550
|
+
|
|
461
551
|
/**
|
|
462
552
|
* Generate struct inside a scope with proper prefixing.
|
|
463
553
|
* Struct fields maintain their original types.
|
|
@@ -473,27 +563,7 @@ function generateScopedStructInline(
|
|
|
473
563
|
|
|
474
564
|
// Process struct members
|
|
475
565
|
for (const member of node.structMember()) {
|
|
476
|
-
|
|
477
|
-
const fieldType = orchestrator.generateType(member.type());
|
|
478
|
-
|
|
479
|
-
// Handle array dimensions if present
|
|
480
|
-
const arrayDims = member.arrayDimension();
|
|
481
|
-
let dimStr = "";
|
|
482
|
-
if (arrayDims.length > 0) {
|
|
483
|
-
dimStr = orchestrator.generateArrayDimensions(arrayDims);
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
// Handle string capacity for string fields
|
|
487
|
-
if (member.type().stringType()) {
|
|
488
|
-
const stringCtx = member.type().stringType()!;
|
|
489
|
-
const intLiteral = stringCtx.INTEGER_LITERAL();
|
|
490
|
-
if (intLiteral) {
|
|
491
|
-
const capacity = Number.parseInt(intLiteral.getText(), 10);
|
|
492
|
-
dimStr += `[${capacity + 1}]`;
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
lines.push(` ${fieldType} ${fieldName}${dimStr};`);
|
|
566
|
+
lines.push(generateScopedStructField(member, orchestrator));
|
|
497
567
|
}
|
|
498
568
|
|
|
499
569
|
lines.push(`} ${fullName};`, "");
|
|
@@ -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
|
+
});
|