c-next 0.1.64 → 0.1.66
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/grammar/CNext.g4 +9 -2
- package/package.json +5 -1
- package/src/transpiler/logic/parser/grammar/CNext.interp +2 -1
- package/src/transpiler/logic/parser/grammar/CNextListener.ts +11 -0
- package/src/transpiler/logic/parser/grammar/CNextParser.ts +992 -870
- package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +7 -0
- package/src/transpiler/logic/symbols/cnext/__tests__/FunctionCollector.test.ts +6 -6
- package/src/transpiler/logic/symbols/cnext/__tests__/TSymbolAdapter.test.ts +179 -0
- package/src/transpiler/logic/symbols/cnext/__tests__/VariableCollector.test.ts +55 -0
- package/src/transpiler/logic/symbols/cnext/adapters/TSymbolAdapter.ts +76 -8
- package/src/transpiler/logic/symbols/cnext/collectors/FunctionCollector.ts +9 -10
- package/src/transpiler/logic/symbols/cnext/collectors/StructCollector.ts +7 -1
- package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +33 -14
- package/src/transpiler/output/codegen/CodeGenerator.ts +243 -166
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +1086 -0
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +254 -22
- package/src/transpiler/output/codegen/generators/declarationGenerators/ArrayDimensionUtils.ts +17 -9
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ArrayDimensionUtils.test.ts +5 -3
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +12 -7
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +624 -12
- package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +819 -0
- package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +337 -0
- package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +135 -0
- package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +4 -0
- package/src/transpiler/output/codegen/helpers/VariableDeclarationFormatter.ts +118 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +426 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +315 -0
- package/src/transpiler/output/codegen/helpers/__tests__/VariableDeclarationFormatter.test.ts +333 -0
- package/src/transpiler/output/codegen/types/IParameterInput.ts +58 -0
- package/src/transpiler/output/codegen/types/IVariableFormatInput.ts +51 -0
- package/src/transpiler/output/headers/BaseHeaderGenerator.ts +20 -35
- package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +21 -48
- package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +0 -64
|
@@ -89,6 +89,7 @@ import { TemplateArgumentListContext } from "./CNextParser.js";
|
|
|
89
89
|
import { TemplateArgumentContext } from "./CNextParser.js";
|
|
90
90
|
import { StringTypeContext } from "./CNextParser.js";
|
|
91
91
|
import { ArrayTypeContext } from "./CNextParser.js";
|
|
92
|
+
import { ArrayTypeDimensionContext } from "./CNextParser.js";
|
|
92
93
|
import { LiteralContext } from "./CNextParser.js";
|
|
93
94
|
|
|
94
95
|
|
|
@@ -616,6 +617,12 @@ export class CNextVisitor<Result> extends AbstractParseTreeVisitor<Result> {
|
|
|
616
617
|
* @return the visitor result
|
|
617
618
|
*/
|
|
618
619
|
visitArrayType?: (ctx: ArrayTypeContext) => Result;
|
|
620
|
+
/**
|
|
621
|
+
* Visit a parse tree produced by `CNextParser.arrayTypeDimension`.
|
|
622
|
+
* @param ctx the parse tree
|
|
623
|
+
* @return the visitor result
|
|
624
|
+
*/
|
|
625
|
+
visitArrayTypeDimension?: (ctx: ArrayTypeDimensionContext) => Result;
|
|
619
626
|
/**
|
|
620
627
|
* Visit a parse tree produced by `CNextParser.literal`.
|
|
621
628
|
* @param ctx the parse tree
|
|
@@ -77,9 +77,9 @@ describe("FunctionCollector", () => {
|
|
|
77
77
|
expect(symbol.parameters[0].isConst).toBe(true);
|
|
78
78
|
});
|
|
79
79
|
|
|
80
|
-
it("handles array parameters", () => {
|
|
80
|
+
it("handles array parameters (C-Next style)", () => {
|
|
81
81
|
const code = `
|
|
82
|
-
void processArray(u8 data
|
|
82
|
+
void processArray(u8[] data) {
|
|
83
83
|
}
|
|
84
84
|
`;
|
|
85
85
|
const tree = parse(code);
|
|
@@ -90,9 +90,9 @@ describe("FunctionCollector", () => {
|
|
|
90
90
|
expect(symbol.parameters[0].arrayDimensions).toEqual([""]);
|
|
91
91
|
});
|
|
92
92
|
|
|
93
|
-
it("handles sized array parameters", () => {
|
|
93
|
+
it("handles sized array parameters (C-Next style)", () => {
|
|
94
94
|
const code = `
|
|
95
|
-
void processBuffer(u8
|
|
95
|
+
void processBuffer(u8[256] buffer) {
|
|
96
96
|
}
|
|
97
97
|
`;
|
|
98
98
|
const tree = parse(code);
|
|
@@ -103,9 +103,9 @@ describe("FunctionCollector", () => {
|
|
|
103
103
|
expect(symbol.parameters[0].arrayDimensions).toEqual(["256"]);
|
|
104
104
|
});
|
|
105
105
|
|
|
106
|
-
it("handles multi-dimensional array parameters", () => {
|
|
106
|
+
it("handles multi-dimensional array parameters (C-Next style)", () => {
|
|
107
107
|
const code = `
|
|
108
|
-
void processMatrix(f32
|
|
108
|
+
void processMatrix(f32[4][4] matrix) {
|
|
109
109
|
}
|
|
110
110
|
`;
|
|
111
111
|
const tree = parse(code);
|
|
@@ -463,4 +463,183 @@ describe("TSymbolAdapter", () => {
|
|
|
463
463
|
expect(symbolTable.getStructFieldType("Point", "x")).toBe("i32");
|
|
464
464
|
});
|
|
465
465
|
});
|
|
466
|
+
|
|
467
|
+
describe("enum member resolution in array dimensions", () => {
|
|
468
|
+
it("resolves unqualified enum member in variable array dimension", () => {
|
|
469
|
+
const enumSym: IEnumSymbol = {
|
|
470
|
+
kind: ESymbolKind.Enum,
|
|
471
|
+
name: "EColor",
|
|
472
|
+
sourceFile: "test.cnx",
|
|
473
|
+
sourceLine: 1,
|
|
474
|
+
sourceLanguage: ESourceLanguage.CNext,
|
|
475
|
+
isExported: true,
|
|
476
|
+
members: new Map([
|
|
477
|
+
["RED", 0],
|
|
478
|
+
["GREEN", 1],
|
|
479
|
+
["BLUE", 2],
|
|
480
|
+
["COUNT", 3],
|
|
481
|
+
]),
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
const variable: IVariableSymbol = {
|
|
485
|
+
kind: ESymbolKind.Variable,
|
|
486
|
+
name: "DATA",
|
|
487
|
+
sourceFile: "test.cnx",
|
|
488
|
+
sourceLine: 10,
|
|
489
|
+
sourceLanguage: ESourceLanguage.CNext,
|
|
490
|
+
isExported: true,
|
|
491
|
+
type: "u8",
|
|
492
|
+
isConst: true,
|
|
493
|
+
isAtomic: false,
|
|
494
|
+
isArray: true,
|
|
495
|
+
arrayDimensions: ["COUNT"], // Unqualified enum member
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
const result = TSymbolAdapter.toISymbols(
|
|
499
|
+
[enumSym, variable],
|
|
500
|
+
symbolTable,
|
|
501
|
+
);
|
|
502
|
+
|
|
503
|
+
// Find the variable symbol
|
|
504
|
+
const varSym = result.find(
|
|
505
|
+
(s) => s.kind === ESymbolKind.Variable && s.name === "DATA",
|
|
506
|
+
);
|
|
507
|
+
expect(varSym).toBeDefined();
|
|
508
|
+
expect(varSym!.arrayDimensions).toEqual(["EColor_COUNT"]);
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
it("resolves enum member in function parameter array dimension", () => {
|
|
512
|
+
const enumSym: IEnumSymbol = {
|
|
513
|
+
kind: ESymbolKind.Enum,
|
|
514
|
+
name: "Size",
|
|
515
|
+
sourceFile: "test.cnx",
|
|
516
|
+
sourceLine: 1,
|
|
517
|
+
sourceLanguage: ESourceLanguage.CNext,
|
|
518
|
+
isExported: true,
|
|
519
|
+
members: new Map([
|
|
520
|
+
["SMALL", 10],
|
|
521
|
+
["MEDIUM", 20],
|
|
522
|
+
["LARGE", 30],
|
|
523
|
+
]),
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
const funcSym: IFunctionSymbol = {
|
|
527
|
+
kind: ESymbolKind.Function,
|
|
528
|
+
name: "process",
|
|
529
|
+
sourceFile: "test.cnx",
|
|
530
|
+
sourceLine: 5,
|
|
531
|
+
sourceLanguage: ESourceLanguage.CNext,
|
|
532
|
+
isExported: true,
|
|
533
|
+
returnType: "void",
|
|
534
|
+
visibility: "public",
|
|
535
|
+
parameters: [
|
|
536
|
+
{
|
|
537
|
+
name: "buffer",
|
|
538
|
+
type: "u8",
|
|
539
|
+
isConst: false,
|
|
540
|
+
isArray: true,
|
|
541
|
+
arrayDimensions: ["MEDIUM"], // Unqualified enum member
|
|
542
|
+
},
|
|
543
|
+
],
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
const result = TSymbolAdapter.toISymbols([enumSym, funcSym], symbolTable);
|
|
547
|
+
|
|
548
|
+
// Find the function symbol
|
|
549
|
+
const funcResult = result.find(
|
|
550
|
+
(s) => s.kind === ESymbolKind.Function && s.name === "process",
|
|
551
|
+
);
|
|
552
|
+
expect(funcResult).toBeDefined();
|
|
553
|
+
expect(funcResult!.parameters).toBeDefined();
|
|
554
|
+
expect(funcResult!.parameters![0].arrayDimensions).toEqual([
|
|
555
|
+
"Size_MEDIUM",
|
|
556
|
+
]);
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
it("does not resolve ambiguous enum member (exists in multiple enums)", () => {
|
|
560
|
+
const enum1: IEnumSymbol = {
|
|
561
|
+
kind: ESymbolKind.Enum,
|
|
562
|
+
name: "EColor",
|
|
563
|
+
sourceFile: "test.cnx",
|
|
564
|
+
sourceLine: 1,
|
|
565
|
+
sourceLanguage: ESourceLanguage.CNext,
|
|
566
|
+
isExported: true,
|
|
567
|
+
members: new Map([["COUNT", 3]]),
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
const enum2: IEnumSymbol = {
|
|
571
|
+
kind: ESymbolKind.Enum,
|
|
572
|
+
name: "ESize",
|
|
573
|
+
sourceFile: "test.cnx",
|
|
574
|
+
sourceLine: 5,
|
|
575
|
+
sourceLanguage: ESourceLanguage.CNext,
|
|
576
|
+
isExported: true,
|
|
577
|
+
members: new Map([
|
|
578
|
+
["COUNT", 5], // Same member name in different enum
|
|
579
|
+
]),
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
const variable: IVariableSymbol = {
|
|
583
|
+
kind: ESymbolKind.Variable,
|
|
584
|
+
name: "DATA",
|
|
585
|
+
sourceFile: "test.cnx",
|
|
586
|
+
sourceLine: 10,
|
|
587
|
+
sourceLanguage: ESourceLanguage.CNext,
|
|
588
|
+
isExported: true,
|
|
589
|
+
type: "u8",
|
|
590
|
+
isConst: true,
|
|
591
|
+
isAtomic: false,
|
|
592
|
+
isArray: true,
|
|
593
|
+
arrayDimensions: ["COUNT"], // Ambiguous - exists in both enums
|
|
594
|
+
};
|
|
595
|
+
|
|
596
|
+
const result = TSymbolAdapter.toISymbols(
|
|
597
|
+
[enum1, enum2, variable],
|
|
598
|
+
symbolTable,
|
|
599
|
+
);
|
|
600
|
+
|
|
601
|
+
// Find the variable symbol - dimension should NOT be resolved
|
|
602
|
+
const varSym = result.find(
|
|
603
|
+
(s) => s.kind === ESymbolKind.Variable && s.name === "DATA",
|
|
604
|
+
);
|
|
605
|
+
expect(varSym).toBeDefined();
|
|
606
|
+
expect(varSym!.arrayDimensions).toEqual(["COUNT"]); // Left as-is
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
it("preserves numeric array dimensions", () => {
|
|
610
|
+
const enumSym: IEnumSymbol = {
|
|
611
|
+
kind: ESymbolKind.Enum,
|
|
612
|
+
name: "EColor",
|
|
613
|
+
sourceFile: "test.cnx",
|
|
614
|
+
sourceLine: 1,
|
|
615
|
+
sourceLanguage: ESourceLanguage.CNext,
|
|
616
|
+
isExported: true,
|
|
617
|
+
members: new Map([["COUNT", 3]]),
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
const variable: IVariableSymbol = {
|
|
621
|
+
kind: ESymbolKind.Variable,
|
|
622
|
+
name: "DATA",
|
|
623
|
+
sourceFile: "test.cnx",
|
|
624
|
+
sourceLine: 10,
|
|
625
|
+
sourceLanguage: ESourceLanguage.CNext,
|
|
626
|
+
isExported: true,
|
|
627
|
+
type: "u8",
|
|
628
|
+
isConst: true,
|
|
629
|
+
isAtomic: false,
|
|
630
|
+
isArray: true,
|
|
631
|
+
arrayDimensions: [256], // Numeric dimension
|
|
632
|
+
};
|
|
633
|
+
|
|
634
|
+
const result = TSymbolAdapter.toISymbols(
|
|
635
|
+
[enumSym, variable],
|
|
636
|
+
symbolTable,
|
|
637
|
+
);
|
|
638
|
+
|
|
639
|
+
const varSym = result.find(
|
|
640
|
+
(s) => s.kind === ESymbolKind.Variable && s.name === "DATA",
|
|
641
|
+
);
|
|
642
|
+
expect(varSym!.arrayDimensions).toEqual(["256"]);
|
|
643
|
+
});
|
|
644
|
+
});
|
|
466
645
|
});
|
|
@@ -156,6 +156,61 @@ describe("VariableCollector", () => {
|
|
|
156
156
|
expect(symbol.isArray).toBe(true);
|
|
157
157
|
expect(symbol.arrayDimensions).toEqual([10, 20]);
|
|
158
158
|
});
|
|
159
|
+
|
|
160
|
+
it("collects C-Next style array with dimensions in type (u8[8] arr)", () => {
|
|
161
|
+
const code = `
|
|
162
|
+
u8[8] buffer;
|
|
163
|
+
`;
|
|
164
|
+
const tree = parse(code);
|
|
165
|
+
const varCtx = tree.declaration(0)!.variableDeclaration()!;
|
|
166
|
+
const symbol = VariableCollector.collect(varCtx, "test.cnx");
|
|
167
|
+
|
|
168
|
+
expect(symbol.isArray).toBe(true);
|
|
169
|
+
expect(symbol.arrayDimensions).toEqual([8]);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("collects C-Next style multi-dimensional array (u8[4][4] arr)", () => {
|
|
173
|
+
const code = `
|
|
174
|
+
u8[4][4] matrix;
|
|
175
|
+
`;
|
|
176
|
+
const tree = parse(code);
|
|
177
|
+
const varCtx = tree.declaration(0)!.variableDeclaration()!;
|
|
178
|
+
const symbol = VariableCollector.collect(varCtx, "test.cnx");
|
|
179
|
+
|
|
180
|
+
expect(symbol.isArray).toBe(true);
|
|
181
|
+
expect(symbol.arrayDimensions).toEqual([4, 4]);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("collects C-Next style array with const reference dimension", () => {
|
|
185
|
+
const code = `
|
|
186
|
+
u8[SIZE] buffer;
|
|
187
|
+
`;
|
|
188
|
+
const tree = parse(code);
|
|
189
|
+
const varCtx = tree.declaration(0)!.variableDeclaration()!;
|
|
190
|
+
const constValues = new Map<string, number>([["SIZE", 16]]);
|
|
191
|
+
const symbol = VariableCollector.collect(
|
|
192
|
+
varCtx,
|
|
193
|
+
"test.cnx",
|
|
194
|
+
undefined,
|
|
195
|
+
true,
|
|
196
|
+
constValues,
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
expect(symbol.isArray).toBe(true);
|
|
200
|
+
expect(symbol.arrayDimensions).toEqual([16]);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it("preserves unresolved macro as string in C-Next style array", () => {
|
|
204
|
+
const code = `
|
|
205
|
+
u8[BUFFER_SIZE] buffer;
|
|
206
|
+
`;
|
|
207
|
+
const tree = parse(code);
|
|
208
|
+
const varCtx = tree.declaration(0)!.variableDeclaration()!;
|
|
209
|
+
const symbol = VariableCollector.collect(varCtx, "test.cnx");
|
|
210
|
+
|
|
211
|
+
expect(symbol.isArray).toBe(true);
|
|
212
|
+
expect(symbol.arrayDimensions).toEqual(["BUFFER_SIZE"]);
|
|
213
|
+
});
|
|
159
214
|
});
|
|
160
215
|
|
|
161
216
|
describe("scoped variables", () => {
|
|
@@ -40,6 +40,9 @@ class TSymbolAdapter {
|
|
|
40
40
|
* @returns Array of flat ISymbol objects for Pipeline consumption
|
|
41
41
|
*/
|
|
42
42
|
static toISymbols(symbols: TSymbol[], symbolTable: SymbolTable): ISymbol[] {
|
|
43
|
+
// First pass: Build enum member lookup for array dimension resolution
|
|
44
|
+
const enumMemberLookup = TSymbolAdapter.buildEnumMemberLookup(symbols);
|
|
45
|
+
|
|
43
46
|
const result: ISymbol[] = [];
|
|
44
47
|
|
|
45
48
|
for (const symbol of symbols) {
|
|
@@ -54,10 +57,12 @@ class TSymbolAdapter {
|
|
|
54
57
|
result.push(TSymbolAdapter.convertStruct(symbol, symbolTable));
|
|
55
58
|
break;
|
|
56
59
|
case ESymbolKind.Function:
|
|
57
|
-
result.push(
|
|
60
|
+
result.push(
|
|
61
|
+
...TSymbolAdapter.convertFunction(symbol, enumMemberLookup),
|
|
62
|
+
);
|
|
58
63
|
break;
|
|
59
64
|
case ESymbolKind.Variable:
|
|
60
|
-
result.push(TSymbolAdapter.convertVariable(symbol));
|
|
65
|
+
result.push(TSymbolAdapter.convertVariable(symbol, enumMemberLookup));
|
|
61
66
|
break;
|
|
62
67
|
case ESymbolKind.Register:
|
|
63
68
|
result.push(...TSymbolAdapter.convertRegister(symbol));
|
|
@@ -71,6 +76,57 @@ class TSymbolAdapter {
|
|
|
71
76
|
return result;
|
|
72
77
|
}
|
|
73
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Build a lookup map from enum member names to their fully-qualified enum names.
|
|
81
|
+
* Used to resolve unqualified enum members in array dimensions.
|
|
82
|
+
*
|
|
83
|
+
* If a member name exists in multiple enums, it's marked as ambiguous (null value)
|
|
84
|
+
* and will not be resolved (the header will fail to compile, surfacing the issue).
|
|
85
|
+
*/
|
|
86
|
+
private static buildEnumMemberLookup(
|
|
87
|
+
symbols: TSymbol[],
|
|
88
|
+
): Map<string, string | null> {
|
|
89
|
+
const lookup = new Map<string, string | null>();
|
|
90
|
+
|
|
91
|
+
for (const symbol of symbols) {
|
|
92
|
+
if (symbol.kind !== ESymbolKind.Enum) continue;
|
|
93
|
+
|
|
94
|
+
for (const memberName of symbol.members.keys()) {
|
|
95
|
+
if (lookup.has(memberName)) {
|
|
96
|
+
// Ambiguous: member exists in multiple enums
|
|
97
|
+
lookup.set(memberName, null);
|
|
98
|
+
} else {
|
|
99
|
+
lookup.set(memberName, symbol.name);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return lookup;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Resolve an array dimension string, adding enum prefix if it's an unqualified enum member.
|
|
109
|
+
*/
|
|
110
|
+
private static resolveArrayDimension(
|
|
111
|
+
dim: number | string,
|
|
112
|
+
enumMemberLookup: Map<string, string | null>,
|
|
113
|
+
): string {
|
|
114
|
+
// Numeric dimensions don't need resolution
|
|
115
|
+
if (typeof dim === "number") {
|
|
116
|
+
return String(dim);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Check if this is an unqualified enum member
|
|
120
|
+
const enumName = enumMemberLookup.get(dim);
|
|
121
|
+
if (enumName) {
|
|
122
|
+
// Found in exactly one enum - add the prefix
|
|
123
|
+
return `${enumName}_${dim}`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Not an enum member, or ambiguous - return as-is
|
|
127
|
+
return dim;
|
|
128
|
+
}
|
|
129
|
+
|
|
74
130
|
/**
|
|
75
131
|
* Convert IBitmapSymbol to ISymbol + BitmapField symbols.
|
|
76
132
|
*/
|
|
@@ -180,21 +236,27 @@ class TSymbolAdapter {
|
|
|
180
236
|
|
|
181
237
|
/**
|
|
182
238
|
* Convert IFunctionSymbol to ISymbol + parameter symbols for hover support.
|
|
239
|
+
* Resolves unqualified enum members in parameter array dimensions.
|
|
183
240
|
*/
|
|
184
|
-
private static convertFunction(
|
|
241
|
+
private static convertFunction(
|
|
242
|
+
func: IFunctionSymbol,
|
|
243
|
+
enumMemberLookup: Map<string, string | null>,
|
|
244
|
+
): ISymbol[] {
|
|
185
245
|
const result: ISymbol[] = [];
|
|
186
246
|
|
|
187
247
|
// Build parameter types for signature
|
|
188
248
|
const paramTypes = func.parameters.map((p) => p.type);
|
|
189
249
|
const signature = `${func.returnType} ${func.name}(${paramTypes.join(", ")})`;
|
|
190
250
|
|
|
191
|
-
// Build parameter info for header generation
|
|
251
|
+
// Build parameter info for header generation, resolving enum members in dimensions
|
|
192
252
|
const parameters = func.parameters.map((p) => ({
|
|
193
253
|
name: p.name,
|
|
194
254
|
type: p.type,
|
|
195
255
|
isConst: p.isConst,
|
|
196
256
|
isArray: p.isArray,
|
|
197
|
-
arrayDimensions: p.arrayDimensions
|
|
257
|
+
arrayDimensions: p.arrayDimensions?.map((dim) =>
|
|
258
|
+
TSymbolAdapter.resolveArrayDimension(dim, enumMemberLookup),
|
|
259
|
+
),
|
|
198
260
|
isAutoConst: p.isAutoConst,
|
|
199
261
|
}));
|
|
200
262
|
|
|
@@ -233,10 +295,16 @@ class TSymbolAdapter {
|
|
|
233
295
|
|
|
234
296
|
/**
|
|
235
297
|
* Convert IVariableSymbol to ISymbol.
|
|
298
|
+
* Resolves unqualified enum members in array dimensions.
|
|
236
299
|
*/
|
|
237
|
-
private static convertVariable(
|
|
238
|
-
|
|
239
|
-
|
|
300
|
+
private static convertVariable(
|
|
301
|
+
variable: IVariableSymbol,
|
|
302
|
+
enumMemberLookup: Map<string, string | null>,
|
|
303
|
+
): ISymbol {
|
|
304
|
+
// Convert dimensions to string dimensions, resolving enum member references
|
|
305
|
+
const arrayDimensions = variable.arrayDimensions?.map((dim) =>
|
|
306
|
+
TSymbolAdapter.resolveArrayDimension(dim, enumMemberLookup),
|
|
307
|
+
);
|
|
240
308
|
|
|
241
309
|
// Get first dimension for legacy size field (only if numeric)
|
|
242
310
|
const firstDim = variable.arrayDimensions?.[0];
|
|
@@ -70,17 +70,16 @@ class FunctionCollector {
|
|
|
70
70
|
const type = TypeUtils.getTypeName(typeCtx, scopeName);
|
|
71
71
|
const isConst = p.constModifier() !== null;
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
const
|
|
73
|
+
// Check for C-Next style array type (u8[8] param, u8[4][4] param, u8[] param)
|
|
74
|
+
const arrayTypeCtx = typeCtx.arrayType();
|
|
75
|
+
const hasArrayType = arrayTypeCtx !== null;
|
|
75
76
|
|
|
76
|
-
// Extract array dimensions
|
|
77
|
+
// Extract array dimensions from arrayType syntax (supports multi-dimensional)
|
|
77
78
|
const arrayDimensions: string[] = [];
|
|
78
|
-
if (
|
|
79
|
-
for (const dim of
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
const match = regex.exec(text);
|
|
83
|
-
arrayDimensions.push(match ? match[1] : ""); // "" means unbounded
|
|
79
|
+
if (hasArrayType) {
|
|
80
|
+
for (const dim of arrayTypeCtx.arrayTypeDimension()) {
|
|
81
|
+
const sizeExpr = dim.expression();
|
|
82
|
+
arrayDimensions.push(sizeExpr ? sizeExpr.getText() : "");
|
|
84
83
|
}
|
|
85
84
|
}
|
|
86
85
|
|
|
@@ -88,7 +87,7 @@ class FunctionCollector {
|
|
|
88
87
|
name,
|
|
89
88
|
type,
|
|
90
89
|
isConst,
|
|
91
|
-
isArray,
|
|
90
|
+
isArray: hasArrayType,
|
|
92
91
|
};
|
|
93
92
|
|
|
94
93
|
if (arrayDimensions.length > 0) {
|
|
@@ -30,7 +30,13 @@ function processArrayTypeSyntax(
|
|
|
30
30
|
return { isArray: false, dimension: undefined };
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
// Get the first dimension (for backwards compatibility with single-dimension code)
|
|
34
|
+
const dims = arrayTypeCtx.arrayTypeDimension();
|
|
35
|
+
if (dims.length === 0) {
|
|
36
|
+
return { isArray: true, dimension: undefined };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const sizeExpr = dims[0].expression();
|
|
34
40
|
if (!sizeExpr) {
|
|
35
41
|
return { isArray: true, dimension: undefined };
|
|
36
42
|
}
|
|
@@ -71,6 +71,32 @@ class VariableCollector {
|
|
|
71
71
|
return dimensions;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Collect dimensions from C-Next style arrayType syntax (u16[8] arr, u16[4][4] arr).
|
|
76
|
+
*/
|
|
77
|
+
private static collectArrayTypeDimensions(
|
|
78
|
+
arrayTypeCtx: Parser.ArrayTypeContext,
|
|
79
|
+
constValues: Map<string, number> | undefined,
|
|
80
|
+
): (number | string)[] {
|
|
81
|
+
const dimensions: (number | string)[] = [];
|
|
82
|
+
for (const dim of arrayTypeCtx.arrayTypeDimension()) {
|
|
83
|
+
const sizeExpr = dim.expression();
|
|
84
|
+
if (!sizeExpr) continue;
|
|
85
|
+
|
|
86
|
+
const dimText = sizeExpr.getText();
|
|
87
|
+
const literalSize = LiteralUtils.parseIntegerLiteral(dimText);
|
|
88
|
+
if (literalSize !== undefined) {
|
|
89
|
+
dimensions.push(literalSize);
|
|
90
|
+
} else if (constValues?.has(dimText)) {
|
|
91
|
+
dimensions.push(constValues.get(dimText)!);
|
|
92
|
+
} else {
|
|
93
|
+
// Keep as string for macro/enum references
|
|
94
|
+
dimensions.push(dimText);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return dimensions;
|
|
98
|
+
}
|
|
99
|
+
|
|
74
100
|
/**
|
|
75
101
|
* Collect a variable declaration and return an IVariableSymbol.
|
|
76
102
|
*
|
|
@@ -110,21 +136,14 @@ class VariableCollector {
|
|
|
110
136
|
const initExpr = ctx.expression();
|
|
111
137
|
const arrayDimensions: (number | string)[] = [];
|
|
112
138
|
|
|
113
|
-
// Collect
|
|
139
|
+
// Collect dimensions from arrayType syntax (u16[8] arr, u16[4][4] arr, u16[] arr)
|
|
114
140
|
if (hasArrayTypeSyntax) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
} else if (constValues?.has(dimText)) {
|
|
122
|
-
arrayDimensions.push(constValues.get(dimText)!);
|
|
123
|
-
} else {
|
|
124
|
-
// Keep as string for macro/enum references
|
|
125
|
-
arrayDimensions.push(dimText);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
141
|
+
arrayDimensions.push(
|
|
142
|
+
...VariableCollector.collectArrayTypeDimensions(
|
|
143
|
+
arrayTypeCtx,
|
|
144
|
+
constValues,
|
|
145
|
+
),
|
|
146
|
+
);
|
|
128
147
|
}
|
|
129
148
|
|
|
130
149
|
// Collect additional dimensions from arrayDimension syntax
|