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
|
@@ -158,6 +158,47 @@ function handleArrayElementBit(
|
|
|
158
158
|
return `${arrayElement} = (${arrayElement} & ~(${one} << ${bitIndex})) | (${intValue} << ${bitIndex});`;
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Handle bit range through struct chain: devices[0].control[0, 4] <- 15
|
|
163
|
+
*
|
|
164
|
+
* The target is a chain like array[idx].member or struct.field with a
|
|
165
|
+
* bit range subscript [start, width] at the end.
|
|
166
|
+
*/
|
|
167
|
+
function handleStructChainBitRange(
|
|
168
|
+
ctx: IAssignmentContext,
|
|
169
|
+
deps: IHandlerDeps,
|
|
170
|
+
): string {
|
|
171
|
+
validateNotCompound(ctx);
|
|
172
|
+
|
|
173
|
+
// Build the base target from postfixOps, excluding the last one (the bit range)
|
|
174
|
+
const baseId = ctx.identifiers[0];
|
|
175
|
+
const opsBeforeLast = ctx.postfixOps.slice(0, -1);
|
|
176
|
+
|
|
177
|
+
let baseTarget = baseId;
|
|
178
|
+
for (const op of opsBeforeLast) {
|
|
179
|
+
const memberId = op.IDENTIFIER();
|
|
180
|
+
if (memberId) {
|
|
181
|
+
baseTarget += "." + memberId.getText();
|
|
182
|
+
} else {
|
|
183
|
+
const exprs = op.expression();
|
|
184
|
+
if (exprs.length > 0) {
|
|
185
|
+
baseTarget += "[" + deps.generateExpression(exprs[0]) + "]";
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Get start and width from the last postfixOp (the bit range)
|
|
191
|
+
const lastOp = ctx.postfixOps.at(-1)!;
|
|
192
|
+
const bitRangeExprs = lastOp.expression();
|
|
193
|
+
const start = deps.generateExpression(bitRangeExprs[0]);
|
|
194
|
+
const width = deps.generateExpression(bitRangeExprs[1]);
|
|
195
|
+
|
|
196
|
+
// Generate bit range write
|
|
197
|
+
// Limitation: assumes 32-bit types. For 64-bit struct members,
|
|
198
|
+
// would need to track member type through chain.
|
|
199
|
+
return BitUtils.multiBitWrite(baseTarget, start, width, ctx.generatedValue);
|
|
200
|
+
}
|
|
201
|
+
|
|
161
202
|
/**
|
|
162
203
|
* All bit access handlers for registration.
|
|
163
204
|
*/
|
|
@@ -166,6 +207,7 @@ const bitAccessHandlers: ReadonlyArray<[AssignmentKind, TAssignmentHandler]> = [
|
|
|
166
207
|
[AssignmentKind.INTEGER_BIT_RANGE, handleIntegerBitRange],
|
|
167
208
|
[AssignmentKind.STRUCT_MEMBER_BIT, handleStructMemberBit],
|
|
168
209
|
[AssignmentKind.ARRAY_ELEMENT_BIT, handleArrayElementBit],
|
|
210
|
+
[AssignmentKind.STRUCT_CHAIN_BIT_RANGE, handleStructChainBitRange],
|
|
169
211
|
];
|
|
170
212
|
|
|
171
213
|
export default bitAccessHandlers;
|
package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts
CHANGED
|
@@ -88,10 +88,11 @@ describe("BitAccessHandlers", () => {
|
|
|
88
88
|
expect(kinds).toContain(AssignmentKind.INTEGER_BIT_RANGE);
|
|
89
89
|
expect(kinds).toContain(AssignmentKind.STRUCT_MEMBER_BIT);
|
|
90
90
|
expect(kinds).toContain(AssignmentKind.ARRAY_ELEMENT_BIT);
|
|
91
|
+
expect(kinds).toContain(AssignmentKind.STRUCT_CHAIN_BIT_RANGE);
|
|
91
92
|
});
|
|
92
93
|
|
|
93
|
-
it("exports exactly
|
|
94
|
-
expect(bitAccessHandlers.length).toBe(
|
|
94
|
+
it("exports exactly 5 handlers", () => {
|
|
95
|
+
expect(bitAccessHandlers.length).toBe(5);
|
|
95
96
|
});
|
|
96
97
|
});
|
|
97
98
|
|
|
@@ -470,4 +471,77 @@ describe("BitAccessHandlers", () => {
|
|
|
470
471
|
);
|
|
471
472
|
});
|
|
472
473
|
});
|
|
474
|
+
|
|
475
|
+
describe("handleStructChainBitRange (STRUCT_CHAIN_BIT_RANGE)", () => {
|
|
476
|
+
const getHandler = () =>
|
|
477
|
+
bitAccessHandlers.find(
|
|
478
|
+
([kind]) => kind === AssignmentKind.STRUCT_CHAIN_BIT_RANGE,
|
|
479
|
+
)?.[1];
|
|
480
|
+
|
|
481
|
+
it("generates bit range write through struct chain", () => {
|
|
482
|
+
const deps = createMockDeps({
|
|
483
|
+
generateExpression: vi
|
|
484
|
+
.fn()
|
|
485
|
+
.mockReturnValueOnce("0") // array index
|
|
486
|
+
.mockReturnValueOnce("0") // bit range start
|
|
487
|
+
.mockReturnValueOnce("4"), // bit range width
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
// Create mock postfixOps for devices[0].control[0, 4]
|
|
491
|
+
const mockPostfixOps = [
|
|
492
|
+
{
|
|
493
|
+
IDENTIFIER: () => null,
|
|
494
|
+
expression: () => [{ mockValue: "0" }],
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
IDENTIFIER: () => ({ getText: () => "control" }),
|
|
498
|
+
expression: () => [],
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
IDENTIFIER: () => null,
|
|
502
|
+
expression: () => [{ mockValue: "0" }, { mockValue: "4" }],
|
|
503
|
+
},
|
|
504
|
+
];
|
|
505
|
+
|
|
506
|
+
const ctx = createMockContext({
|
|
507
|
+
identifiers: ["devices", "control"],
|
|
508
|
+
subscripts: [
|
|
509
|
+
{ mockValue: "0" } as never,
|
|
510
|
+
{ mockValue: "0" } as never,
|
|
511
|
+
{ mockValue: "4" } as never,
|
|
512
|
+
],
|
|
513
|
+
postfixOps: mockPostfixOps as never,
|
|
514
|
+
generatedValue: "15",
|
|
515
|
+
hasMemberAccess: true,
|
|
516
|
+
hasArrayAccess: true,
|
|
517
|
+
lastSubscriptExprCount: 2,
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
const result = getHandler()!(ctx, deps);
|
|
521
|
+
|
|
522
|
+
expect(result).toContain("devices[0].control =");
|
|
523
|
+
expect(result).toContain("& ~(");
|
|
524
|
+
expect(result).toContain("<< 0");
|
|
525
|
+
expect(result).toContain("15");
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
it("throws on compound assignment", () => {
|
|
529
|
+
const deps = createMockDeps();
|
|
530
|
+
const mockPostfixOps = [
|
|
531
|
+
{
|
|
532
|
+
IDENTIFIER: () => null,
|
|
533
|
+
expression: () => [{ mockValue: "0" }, { mockValue: "4" }],
|
|
534
|
+
},
|
|
535
|
+
];
|
|
536
|
+
const ctx = createMockContext({
|
|
537
|
+
isCompound: true,
|
|
538
|
+
cnextOp: "+<-",
|
|
539
|
+
postfixOps: mockPostfixOps as never,
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
expect(() => getHandler()!(ctx, deps)).toThrow(
|
|
543
|
+
"Compound assignment operators not supported for bit field access",
|
|
544
|
+
);
|
|
545
|
+
});
|
|
546
|
+
});
|
|
473
547
|
});
|
|
@@ -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
|
// =========================================================================
|
|
@@ -120,7 +120,11 @@ interface IOrchestrator {
|
|
|
120
120
|
// === Validation ===
|
|
121
121
|
|
|
122
122
|
/** Validate cross-scope member visibility (ADR-016) */
|
|
123
|
-
validateCrossScopeVisibility(
|
|
123
|
+
validateCrossScopeVisibility(
|
|
124
|
+
scopeName: string,
|
|
125
|
+
memberName: string,
|
|
126
|
+
isGlobalAccess?: boolean,
|
|
127
|
+
): void;
|
|
124
128
|
|
|
125
129
|
/** Validate shift amount is within type bounds */
|
|
126
130
|
validateShiftAmount(
|
|
@@ -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;
|
|
@@ -20,6 +20,7 @@ import IGeneratorState from "../IGeneratorState";
|
|
|
20
20
|
import IGeneratorOutput from "../IGeneratorOutput";
|
|
21
21
|
import IOrchestrator from "../IOrchestrator";
|
|
22
22
|
import TGeneratorFn from "../TGeneratorFn";
|
|
23
|
+
import generateRegisterMacros from "./RegisterMacroGenerator";
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* Generate C #define macros from a C-Next register declaration.
|
|
@@ -36,30 +37,16 @@ const generateRegister: TGeneratorFn<Parser.RegisterDeclarationContext> = (
|
|
|
36
37
|
const name = node.IDENTIFIER().getText();
|
|
37
38
|
const baseAddress = orchestrator.generateExpression(node.expression());
|
|
38
39
|
|
|
39
|
-
const lines: string[] = [
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
// Determine qualifiers based on access mode
|
|
51
|
-
let cast = `volatile ${regType}*`;
|
|
52
|
-
if (access === "ro") {
|
|
53
|
-
cast = `volatile ${regType} const *`;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Generate: #define GPIO7_DR (*(volatile uint32_t*)(0x42004000 + 0x00))
|
|
57
|
-
lines.push(
|
|
58
|
-
`#define ${name}_${regName} (*(${cast})(${baseAddress} + ${offset}))`,
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
lines.push("");
|
|
40
|
+
const lines: string[] = [
|
|
41
|
+
`/* Register: ${name} @ ${baseAddress} */`,
|
|
42
|
+
...generateRegisterMacros(
|
|
43
|
+
node.registerMember(),
|
|
44
|
+
name,
|
|
45
|
+
baseAddress,
|
|
46
|
+
orchestrator,
|
|
47
|
+
),
|
|
48
|
+
"",
|
|
49
|
+
];
|
|
63
50
|
|
|
64
51
|
return {
|
|
65
52
|
code: lines.join("\n"),
|
package/src/transpiler/output/codegen/generators/declarationGenerators/RegisterMacroGenerator.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RegisterMacroGenerator - Shared logic for register #define macro generation
|
|
3
|
+
*
|
|
4
|
+
* Extracts common logic from RegisterGenerator and ScopedRegisterGenerator
|
|
5
|
+
* for generating C #define macros from C-Next register members.
|
|
6
|
+
*/
|
|
7
|
+
import * as Parser from "../../../../logic/parser/grammar/CNextParser";
|
|
8
|
+
import IOrchestrator from "../IOrchestrator";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Optional type resolver for scoped types.
|
|
12
|
+
* Returns resolved type name if found, undefined to use original type.
|
|
13
|
+
*/
|
|
14
|
+
type TTypeResolver = (originalType: string) => string | undefined;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Generate #define macros for register members.
|
|
18
|
+
*
|
|
19
|
+
* @param members - Register member declarations from AST
|
|
20
|
+
* @param prefix - Prefix for macro names (e.g., "GPIO7" or "Teensy4_GPIO7")
|
|
21
|
+
* @param baseAddress - Base address expression string
|
|
22
|
+
* @param orchestrator - Code generation orchestrator
|
|
23
|
+
* @param typeResolver - Optional callback to resolve scoped types
|
|
24
|
+
* @returns Array of #define lines
|
|
25
|
+
*/
|
|
26
|
+
function generateRegisterMacros(
|
|
27
|
+
members: Parser.RegisterMemberContext[],
|
|
28
|
+
prefix: string,
|
|
29
|
+
baseAddress: string,
|
|
30
|
+
orchestrator: IOrchestrator,
|
|
31
|
+
typeResolver?: TTypeResolver,
|
|
32
|
+
): string[] {
|
|
33
|
+
const lines: string[] = [];
|
|
34
|
+
|
|
35
|
+
for (const member of members) {
|
|
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
|
+
// Apply optional type resolution (for scoped bitmaps)
|
|
42
|
+
if (typeResolver) {
|
|
43
|
+
const resolved = typeResolver(regType);
|
|
44
|
+
if (resolved) {
|
|
45
|
+
regType = resolved;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Determine qualifiers based on access mode
|
|
50
|
+
let cast = `volatile ${regType}*`;
|
|
51
|
+
if (access === "ro") {
|
|
52
|
+
cast = `volatile ${regType} const *`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Generate: #define PREFIX_REGNAME (*(volatile type*)(base + offset))
|
|
56
|
+
lines.push(
|
|
57
|
+
`#define ${prefix}_${regName} (*(${cast})(${baseAddress} + ${offset}))`,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return lines;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export default generateRegisterMacros;
|
|
@@ -22,6 +22,34 @@ 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
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Extract scoped name from a declaration node.
|
|
44
|
+
* Returns both the local name and the fully qualified scoped name.
|
|
45
|
+
*/
|
|
46
|
+
function getScopedName(
|
|
47
|
+
node: { IDENTIFIER(): { getText(): string } },
|
|
48
|
+
scopeName: string,
|
|
49
|
+
): { name: string; fullName: string } {
|
|
50
|
+
const name = node.IDENTIFIER().getText();
|
|
51
|
+
return { name, fullName: `${scopeName}_${name}` };
|
|
52
|
+
}
|
|
25
53
|
|
|
26
54
|
/**
|
|
27
55
|
* Validate and resolve constructor arguments, ensuring each is const.
|
|
@@ -69,30 +97,25 @@ function generateScopeVariable(
|
|
|
69
97
|
// Issue #375: Check for constructor syntax
|
|
70
98
|
const constructorArgList = varDecl.constructorArgumentList();
|
|
71
99
|
if (constructorArgList) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const prefix = isPrivate ? "static " : "";
|
|
76
|
-
|
|
77
|
-
// Validate and resolve constructor arguments
|
|
78
|
-
const argIdentifiers = constructorArgList.IDENTIFIER();
|
|
79
|
-
const line = varDecl.start?.line ?? 0;
|
|
80
|
-
const resolvedArgs = resolveConstructorArgs(
|
|
81
|
-
argIdentifiers,
|
|
100
|
+
return generateConstructorVariable(
|
|
101
|
+
varDecl,
|
|
102
|
+
varName,
|
|
82
103
|
scopeName,
|
|
83
|
-
|
|
104
|
+
isPrivate,
|
|
105
|
+
constructorArgList,
|
|
84
106
|
orchestrator,
|
|
85
107
|
);
|
|
86
|
-
|
|
87
|
-
return `${prefix}${type} ${fullName}(${resolvedArgs.join(", ")});`;
|
|
88
108
|
}
|
|
89
109
|
|
|
90
110
|
// Issue #282: Check if this is a const variable - const values should be inlined
|
|
91
111
|
const isConst = varDecl.constModifier() !== null;
|
|
92
112
|
|
|
93
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
|
|
94
116
|
const arrayDims = varDecl.arrayDimension();
|
|
95
|
-
const
|
|
117
|
+
const arrayTypeCtx = varDecl.type().arrayType?.() ?? null;
|
|
118
|
+
const isArray = arrayDims.length > 0 || arrayTypeCtx !== null;
|
|
96
119
|
|
|
97
120
|
// Issue #282: Private const variables should be inlined, not emitted at file scope
|
|
98
121
|
// Issue #500: EXCEPT arrays - arrays must be emitted as static const
|
|
@@ -101,6 +124,60 @@ function generateScopeVariable(
|
|
|
101
124
|
return null;
|
|
102
125
|
}
|
|
103
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
|
+
|
|
104
181
|
// ADR-016: All scope variables are emitted at file scope (static-like persistence)
|
|
105
182
|
const type = orchestrator.generateType(varDecl.type());
|
|
106
183
|
const fullName = `${scopeName}_${varName}`;
|
|
@@ -108,26 +185,22 @@ function generateScopeVariable(
|
|
|
108
185
|
const constPrefix = isConst ? "const " : "";
|
|
109
186
|
const prefix = isPrivate ? "static " : "";
|
|
110
187
|
|
|
111
|
-
//
|
|
188
|
+
// Build declaration with all dimensions
|
|
112
189
|
let decl = `${prefix}${constPrefix}${type} ${fullName}`;
|
|
113
|
-
|
|
190
|
+
decl += ArrayDimensionUtils.generateArrayTypeDimension(
|
|
191
|
+
arrayTypeCtx,
|
|
192
|
+
orchestrator,
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
if (arrayDims.length > 0) {
|
|
196
|
+
// C-style or additional dimensions
|
|
114
197
|
decl += orchestrator.generateArrayDimensions(arrayDims);
|
|
115
198
|
}
|
|
199
|
+
|
|
116
200
|
// ADR-045: Add string capacity dimension for string arrays
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (intLiteral) {
|
|
121
|
-
const capacity = Number.parseInt(intLiteral.getText(), 10);
|
|
122
|
-
decl += `[${capacity + 1}]`;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
if (varDecl.expression()) {
|
|
126
|
-
decl += ` = ${orchestrator.generateExpression(varDecl.expression()!)}`;
|
|
127
|
-
} else {
|
|
128
|
-
// ADR-015: Zero initialization for uninitialized scope variables
|
|
129
|
-
decl += ` = ${orchestrator.getZeroInitializer(varDecl.type(), isArray)}`;
|
|
130
|
-
}
|
|
201
|
+
decl += ArrayDimensionUtils.generateStringCapacityDim(varDecl.type());
|
|
202
|
+
decl += generateInitializer(varDecl, isArray, orchestrator);
|
|
203
|
+
|
|
131
204
|
return decl + ";";
|
|
132
205
|
}
|
|
133
206
|
|
|
@@ -339,11 +412,8 @@ function generateScopedEnumInline(
|
|
|
339
412
|
input: IGeneratorInput,
|
|
340
413
|
orchestrator: IOrchestrator,
|
|
341
414
|
): string {
|
|
342
|
-
const
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
const lines: string[] = [];
|
|
346
|
-
lines.push(`typedef enum {`);
|
|
415
|
+
const { fullName } = getScopedName(node, scopeName);
|
|
416
|
+
const lines: string[] = [`typedef enum {`];
|
|
347
417
|
|
|
348
418
|
// Try to get members from symbol info first
|
|
349
419
|
const symbolMembers = input.symbols?.enumMembers.get(fullName);
|
|
@@ -449,6 +519,35 @@ function generateScopedBitmapInline(
|
|
|
449
519
|
return lines.join("\n");
|
|
450
520
|
}
|
|
451
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
|
+
|
|
452
551
|
/**
|
|
453
552
|
* Generate struct inside a scope with proper prefixing.
|
|
454
553
|
* Struct fields maintain their original types.
|
|
@@ -459,35 +558,12 @@ function generateScopedStructInline(
|
|
|
459
558
|
_input: IGeneratorInput,
|
|
460
559
|
orchestrator: IOrchestrator,
|
|
461
560
|
): string {
|
|
462
|
-
const
|
|
463
|
-
const
|
|
464
|
-
|
|
465
|
-
const lines: string[] = [];
|
|
466
|
-
lines.push(`typedef struct ${fullName} {`);
|
|
561
|
+
const { fullName } = getScopedName(node, scopeName);
|
|
562
|
+
const lines: string[] = [`typedef struct ${fullName} {`];
|
|
467
563
|
|
|
468
564
|
// Process struct members
|
|
469
565
|
for (const member of node.structMember()) {
|
|
470
|
-
|
|
471
|
-
const fieldType = orchestrator.generateType(member.type());
|
|
472
|
-
|
|
473
|
-
// Handle array dimensions if present
|
|
474
|
-
const arrayDims = member.arrayDimension();
|
|
475
|
-
let dimStr = "";
|
|
476
|
-
if (arrayDims.length > 0) {
|
|
477
|
-
dimStr = orchestrator.generateArrayDimensions(arrayDims);
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
// Handle string capacity for string fields
|
|
481
|
-
if (member.type().stringType()) {
|
|
482
|
-
const stringCtx = member.type().stringType()!;
|
|
483
|
-
const intLiteral = stringCtx.INTEGER_LITERAL();
|
|
484
|
-
if (intLiteral) {
|
|
485
|
-
const capacity = Number.parseInt(intLiteral.getText(), 10);
|
|
486
|
-
dimStr += `[${capacity + 1}]`;
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
lines.push(` ${fieldType} ${fieldName}${dimStr};`);
|
|
566
|
+
lines.push(generateScopedStructField(member, orchestrator));
|
|
491
567
|
}
|
|
492
568
|
|
|
493
569
|
lines.push(`} ${fullName};`, "");
|