c-next 0.1.6 → 0.1.7
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/package.json +1 -1
- package/src/codegen/CodeGenerator.ts +16 -4
- package/src/pipeline/CacheManager.ts +21 -1
- package/src/pipeline/Pipeline.ts +49 -35
- package/src/pipeline/detectCppSyntax.ts +42 -0
- package/src/pipeline/types/ICachedFileEntry.ts +2 -0
- package/src/symbols/CppSymbolCollector.ts +60 -0
- package/src/symbols/SymbolTable.ts +44 -0
package/package.json
CHANGED
|
@@ -6749,9 +6749,15 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
6749
6749
|
subscriptDepth >= dimensions.length
|
|
6750
6750
|
) {
|
|
6751
6751
|
// Array member fully subscripted (e.g., ts.arr[0][1].length) -> return element bit width
|
|
6752
|
-
// Try C-Next types first, then C types
|
|
6753
|
-
|
|
6752
|
+
// Try C-Next types first, then C types, then enum types
|
|
6753
|
+
let bitWidth =
|
|
6754
6754
|
TYPE_WIDTH[memberType] || C_TYPE_WIDTH[memberType] || 0;
|
|
6755
|
+
// Issue #208: Check if it's a typed enum
|
|
6756
|
+
if (bitWidth === 0 && this.symbolTable) {
|
|
6757
|
+
const enumWidth =
|
|
6758
|
+
this.symbolTable.getEnumBitWidth(memberType);
|
|
6759
|
+
if (enumWidth) bitWidth = enumWidth;
|
|
6760
|
+
}
|
|
6755
6761
|
if (bitWidth > 0) {
|
|
6756
6762
|
result = String(bitWidth);
|
|
6757
6763
|
} else {
|
|
@@ -6759,9 +6765,15 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
6759
6765
|
}
|
|
6760
6766
|
} else {
|
|
6761
6767
|
// Non-array member -> return bit width
|
|
6762
|
-
// Try C-Next types first, then C types
|
|
6763
|
-
|
|
6768
|
+
// Try C-Next types first, then C types, then enum types
|
|
6769
|
+
let bitWidth =
|
|
6764
6770
|
TYPE_WIDTH[memberType] || C_TYPE_WIDTH[memberType] || 0;
|
|
6771
|
+
// Issue #208: Check if it's a typed enum
|
|
6772
|
+
if (bitWidth === 0 && this.symbolTable) {
|
|
6773
|
+
const enumWidth =
|
|
6774
|
+
this.symbolTable.getEnumBitWidth(memberType);
|
|
6775
|
+
if (enumWidth) bitWidth = enumWidth;
|
|
6776
|
+
}
|
|
6765
6777
|
if (bitWidth > 0) {
|
|
6766
6778
|
result = String(bitWidth);
|
|
6767
6779
|
} else {
|
|
@@ -27,7 +27,7 @@ import ICachedFileEntry from "./types/ICachedFileEntry";
|
|
|
27
27
|
import ISerializedSymbol from "./types/ISerializedSymbol";
|
|
28
28
|
|
|
29
29
|
/** Current cache format version - increment when serialization format changes */
|
|
30
|
-
const CACHE_VERSION =
|
|
30
|
+
const CACHE_VERSION = 2; // Issue #208: Added enumBitWidth
|
|
31
31
|
|
|
32
32
|
// Read version from package.json
|
|
33
33
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
@@ -109,6 +109,7 @@ class CacheManager {
|
|
|
109
109
|
symbols: ISymbol[];
|
|
110
110
|
structFields: Map<string, Map<string, IStructFieldInfo>>;
|
|
111
111
|
needsStructKeyword: string[];
|
|
112
|
+
enumBitWidth: Map<string, number>;
|
|
112
113
|
} | null {
|
|
113
114
|
const entry = this.entries.get(filePath);
|
|
114
115
|
if (!entry) {
|
|
@@ -128,10 +129,19 @@ class CacheManager {
|
|
|
128
129
|
structFields.set(structName, fieldMap);
|
|
129
130
|
}
|
|
130
131
|
|
|
132
|
+
// Issue #208: Convert enum bit widths from plain object to Map
|
|
133
|
+
const enumBitWidth = new Map<string, number>();
|
|
134
|
+
if (entry.enumBitWidth) {
|
|
135
|
+
for (const [enumName, width] of Object.entries(entry.enumBitWidth)) {
|
|
136
|
+
enumBitWidth.set(enumName, width);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
131
140
|
return {
|
|
132
141
|
symbols,
|
|
133
142
|
structFields,
|
|
134
143
|
needsStructKeyword: entry.needsStructKeyword ?? [],
|
|
144
|
+
enumBitWidth,
|
|
135
145
|
};
|
|
136
146
|
}
|
|
137
147
|
|
|
@@ -143,6 +153,7 @@ class CacheManager {
|
|
|
143
153
|
symbols: ISymbol[],
|
|
144
154
|
structFields: Map<string, Map<string, IStructFieldInfo>>,
|
|
145
155
|
needsStructKeyword?: string[],
|
|
156
|
+
enumBitWidth?: Map<string, number>,
|
|
146
157
|
): void {
|
|
147
158
|
// Get current mtime
|
|
148
159
|
let mtime: number;
|
|
@@ -169,6 +180,14 @@ class CacheManager {
|
|
|
169
180
|
}
|
|
170
181
|
}
|
|
171
182
|
|
|
183
|
+
// Issue #208: Convert enum bit widths from Map to plain object
|
|
184
|
+
const serializedEnumBitWidth: Record<string, number> = {};
|
|
185
|
+
if (enumBitWidth) {
|
|
186
|
+
for (const [enumName, width] of enumBitWidth) {
|
|
187
|
+
serializedEnumBitWidth[enumName] = width;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
172
191
|
// Create entry
|
|
173
192
|
const entry: ICachedFileEntry = {
|
|
174
193
|
filePath,
|
|
@@ -176,6 +195,7 @@ class CacheManager {
|
|
|
176
195
|
symbols: serializedSymbols,
|
|
177
196
|
structFields: serializedFields,
|
|
178
197
|
needsStructKeyword,
|
|
198
|
+
enumBitWidth: serializedEnumBitWidth,
|
|
179
199
|
};
|
|
180
200
|
|
|
181
201
|
this.entries.set(filePath, entry);
|
package/src/pipeline/Pipeline.ts
CHANGED
|
@@ -26,7 +26,6 @@ import { CPP14Parser } from "../parser/cpp/grammar/CPP14Parser";
|
|
|
26
26
|
import CodeGenerator from "../codegen/CodeGenerator";
|
|
27
27
|
import HeaderGenerator from "../codegen/HeaderGenerator";
|
|
28
28
|
import SymbolTable from "../symbols/SymbolTable";
|
|
29
|
-
import ISymbol from "../types/ISymbol";
|
|
30
29
|
import CNextSymbolCollector from "../symbols/CNextSymbolCollector";
|
|
31
30
|
import CSymbolCollector from "../symbols/CSymbolCollector";
|
|
32
31
|
import CppSymbolCollector from "../symbols/CppSymbolCollector";
|
|
@@ -44,6 +43,7 @@ import IFileResult from "./types/IFileResult";
|
|
|
44
43
|
import runAnalyzers from "./runAnalyzers";
|
|
45
44
|
import CacheManager from "./CacheManager";
|
|
46
45
|
import IStructFieldInfo from "../symbols/types/IStructFieldInfo";
|
|
46
|
+
import detectCppSyntax from "./detectCppSyntax";
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
49
|
* Unified transpilation pipeline
|
|
@@ -312,10 +312,11 @@ class Pipeline {
|
|
|
312
312
|
if (this.cacheManager?.isValid(file.path)) {
|
|
313
313
|
const cached = this.cacheManager.getSymbols(file.path);
|
|
314
314
|
if (cached) {
|
|
315
|
-
// Restore symbols, struct fields, and
|
|
315
|
+
// Restore symbols, struct fields, needsStructKeyword, and enumBitWidth from cache
|
|
316
316
|
this.symbolTable.addSymbols(cached.symbols);
|
|
317
317
|
this.symbolTable.restoreStructFields(cached.structFields);
|
|
318
318
|
this.symbolTable.restoreNeedsStructKeyword(cached.needsStructKeyword);
|
|
319
|
+
this.symbolTable.restoreEnumBitWidths(cached.enumBitWidth);
|
|
319
320
|
return; // Cache hit - skip parsing
|
|
320
321
|
}
|
|
321
322
|
}
|
|
@@ -356,23 +357,35 @@ class Pipeline {
|
|
|
356
357
|
const needsStructKeyword = this.extractNeedsStructKeywordForFile(
|
|
357
358
|
file.path,
|
|
358
359
|
);
|
|
360
|
+
const enumBitWidth = this.extractEnumBitWidthsForFile(file.path);
|
|
359
361
|
this.cacheManager.setSymbols(
|
|
360
362
|
file.path,
|
|
361
363
|
symbols,
|
|
362
364
|
structFields,
|
|
363
365
|
needsStructKeyword,
|
|
366
|
+
enumBitWidth,
|
|
364
367
|
);
|
|
365
368
|
}
|
|
366
369
|
}
|
|
367
370
|
|
|
368
371
|
/**
|
|
369
|
-
* Parse a C header using
|
|
372
|
+
* Issue #208: Parse a C header using single-parser strategy
|
|
373
|
+
* Uses heuristic detection to choose the appropriate parser
|
|
370
374
|
*/
|
|
371
375
|
private parseCHeader(content: string, filePath: string): void {
|
|
372
|
-
|
|
373
|
-
|
|
376
|
+
if (detectCppSyntax(content)) {
|
|
377
|
+
// Use C++14 parser for headers with C++ syntax (typed enums, classes, etc.)
|
|
378
|
+
this.parseCppHeader(content, filePath);
|
|
379
|
+
} else {
|
|
380
|
+
// Use C parser for pure C headers
|
|
381
|
+
this.parsePureCHeader(content, filePath);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
374
384
|
|
|
375
|
-
|
|
385
|
+
/**
|
|
386
|
+
* Issue #208: Parse a pure C header (no C++ syntax detected)
|
|
387
|
+
*/
|
|
388
|
+
private parsePureCHeader(content: string, filePath: string): void {
|
|
376
389
|
try {
|
|
377
390
|
const charStream = CharStream.fromString(content);
|
|
378
391
|
const lexer = new CLexer(charStream);
|
|
@@ -382,36 +395,12 @@ class Pipeline {
|
|
|
382
395
|
|
|
383
396
|
const tree = parser.compilationUnit();
|
|
384
397
|
const collector = new CSymbolCollector(filePath, this.symbolTable);
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
// Also try C++ parser for better C++11 support
|
|
391
|
-
try {
|
|
392
|
-
const cppCharStream = CharStream.fromString(content);
|
|
393
|
-
const cppLexer = new CPP14Lexer(cppCharStream);
|
|
394
|
-
const cppTokenStream = new CommonTokenStream(cppLexer);
|
|
395
|
-
const cppParser = new CPP14Parser(cppTokenStream);
|
|
396
|
-
cppParser.removeErrorListeners();
|
|
397
|
-
|
|
398
|
-
const cppTree = cppParser.translationUnit();
|
|
399
|
-
const cppCollector = new CppSymbolCollector(filePath, this.symbolTable);
|
|
400
|
-
cppSymbols = cppCollector.collect(cppTree);
|
|
398
|
+
const symbols = collector.collect(tree);
|
|
399
|
+
if (symbols.length > 0) {
|
|
400
|
+
this.symbolTable.addSymbols(symbols);
|
|
401
|
+
}
|
|
401
402
|
} catch {
|
|
402
|
-
//
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
// Merge symbols: prefer C++ but supplement with C-only symbols
|
|
406
|
-
const cppSymbolNames = new Set(cppSymbols.map((s) => s.name));
|
|
407
|
-
const additionalSymbols = cSymbols.filter(
|
|
408
|
-
(s) => !cppSymbolNames.has(s.name),
|
|
409
|
-
);
|
|
410
|
-
|
|
411
|
-
const allSymbols = [...cppSymbols, ...additionalSymbols];
|
|
412
|
-
|
|
413
|
-
if (allSymbols.length > 0) {
|
|
414
|
-
this.symbolTable.addSymbols(allSymbols);
|
|
403
|
+
// Silently ignore parse errors in headers
|
|
415
404
|
}
|
|
416
405
|
}
|
|
417
406
|
|
|
@@ -961,6 +950,31 @@ class Pipeline {
|
|
|
961
950
|
const allNeedsKeyword = this.symbolTable.getAllNeedsStructKeyword();
|
|
962
951
|
return structNames.filter((name) => allNeedsKeyword.includes(name));
|
|
963
952
|
}
|
|
953
|
+
|
|
954
|
+
/**
|
|
955
|
+
* Issue #208: Extract enum bit widths for a specific file
|
|
956
|
+
* Returns enum bit widths for enums defined in that file
|
|
957
|
+
*/
|
|
958
|
+
private extractEnumBitWidthsForFile(filePath: string): Map<string, number> {
|
|
959
|
+
const result = new Map<string, number>();
|
|
960
|
+
|
|
961
|
+
// Get enum names defined in this file
|
|
962
|
+
const fileSymbols = this.symbolTable.getSymbolsByFile(filePath);
|
|
963
|
+
const enumNames = fileSymbols
|
|
964
|
+
.filter((s) => s.kind === "enum")
|
|
965
|
+
.map((s) => s.name);
|
|
966
|
+
|
|
967
|
+
// Get bit widths for each enum
|
|
968
|
+
const allBitWidths = this.symbolTable.getAllEnumBitWidths();
|
|
969
|
+
for (const enumName of enumNames) {
|
|
970
|
+
const width = allBitWidths.get(enumName);
|
|
971
|
+
if (width !== undefined) {
|
|
972
|
+
result.set(enumName, width);
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
return result;
|
|
977
|
+
}
|
|
964
978
|
}
|
|
965
979
|
|
|
966
980
|
export default Pipeline;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Issue #208: Detect if header content contains C++ syntax requiring C++14 parser
|
|
3
|
+
*
|
|
4
|
+
* This heuristic determines whether to parse a .h file with the C or C++ parser.
|
|
5
|
+
* Previously, the pipeline would parse ALL .h files with BOTH parsers and merge,
|
|
6
|
+
* which was wasteful and led to issues with C++-specific features like typed enums.
|
|
7
|
+
*
|
|
8
|
+
* C++ indicators:
|
|
9
|
+
* - Typed enums: enum Name : type { ... }
|
|
10
|
+
* - Class/struct inheritance: class Foo : public Bar
|
|
11
|
+
* - Namespaces: namespace Foo { ... }
|
|
12
|
+
* - Templates: template<...>
|
|
13
|
+
* - Access specifiers: public:, private:, protected:
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Detect if header content contains C++ syntax requiring C++14 parser
|
|
18
|
+
* @param content Raw header file content
|
|
19
|
+
* @returns true if C++ parser should be used, false for C parser
|
|
20
|
+
*/
|
|
21
|
+
function detectCppSyntax(content: string): boolean {
|
|
22
|
+
// Typed enums: enum Name : type { (C++14 feature, key for Issue #208)
|
|
23
|
+
if (/enum\s+\w+\s*:\s*\w+\s*\{/.test(content)) return true;
|
|
24
|
+
|
|
25
|
+
// Class/struct with inheritance: class Foo : public/private/protected Bar
|
|
26
|
+
if (/\b(class|struct)\s+\w+\s*:\s*(public|private|protected)/.test(content))
|
|
27
|
+
return true;
|
|
28
|
+
|
|
29
|
+
// namespace keyword: namespace Foo {
|
|
30
|
+
if (/\bnamespace\s+\w+/.test(content)) return true;
|
|
31
|
+
|
|
32
|
+
// template declarations: template<...>
|
|
33
|
+
if (/\btemplate\s*</.test(content)) return true;
|
|
34
|
+
|
|
35
|
+
// Access specifiers at line start (class members)
|
|
36
|
+
if (/^\s*(public|private|protected)\s*:/m.test(content)) return true;
|
|
37
|
+
|
|
38
|
+
// Default to C parser for pure C headers
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default detectCppSyntax;
|
|
@@ -16,6 +16,8 @@ interface ICachedFileEntry {
|
|
|
16
16
|
structFields: Record<string, Record<string, IStructFieldInfo>>;
|
|
17
17
|
/** Issue #196 Bug 3: Struct names requiring 'struct' keyword in C */
|
|
18
18
|
needsStructKeyword?: string[];
|
|
19
|
+
/** Issue #208: Enum bit widths from typed enums (enum name -> bit width) */
|
|
20
|
+
enumBitWidth?: Record<string, number>;
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
export default ICachedFileEntry;
|
|
@@ -384,6 +384,66 @@ class CppSymbolCollector {
|
|
|
384
384
|
isExported: true,
|
|
385
385
|
parent: this.currentNamespace,
|
|
386
386
|
});
|
|
387
|
+
|
|
388
|
+
// Issue #208: Extract enum backing type for typed enums (e.g., enum EPressureType : uint8_t)
|
|
389
|
+
if (this.symbolTable) {
|
|
390
|
+
const enumbase = enumHead.enumbase?.();
|
|
391
|
+
if (enumbase) {
|
|
392
|
+
const typeSpecSeq = enumbase.typeSpecifierSeq?.();
|
|
393
|
+
if (typeSpecSeq) {
|
|
394
|
+
const typeName = typeSpecSeq.getText();
|
|
395
|
+
const bitWidth = this.getTypeWidth(typeName);
|
|
396
|
+
if (bitWidth > 0) {
|
|
397
|
+
this.symbolTable.addEnumBitWidth(fullName, bitWidth);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Issue #208: Map C/C++ type names to their bit widths
|
|
406
|
+
* Supports standard integer types used as enum backing types
|
|
407
|
+
*/
|
|
408
|
+
private getTypeWidth(typeName: string): number {
|
|
409
|
+
const typeWidths: Record<string, number> = {
|
|
410
|
+
// stdint.h types
|
|
411
|
+
uint8_t: 8,
|
|
412
|
+
int8_t: 8,
|
|
413
|
+
uint16_t: 16,
|
|
414
|
+
int16_t: 16,
|
|
415
|
+
uint32_t: 32,
|
|
416
|
+
int32_t: 32,
|
|
417
|
+
uint64_t: 64,
|
|
418
|
+
int64_t: 64,
|
|
419
|
+
// Standard C types (common sizes)
|
|
420
|
+
char: 8,
|
|
421
|
+
"signed char": 8,
|
|
422
|
+
"unsigned char": 8,
|
|
423
|
+
short: 16,
|
|
424
|
+
"short int": 16,
|
|
425
|
+
"signed short": 16,
|
|
426
|
+
"signed short int": 16,
|
|
427
|
+
"unsigned short": 16,
|
|
428
|
+
"unsigned short int": 16,
|
|
429
|
+
int: 32,
|
|
430
|
+
"signed int": 32,
|
|
431
|
+
unsigned: 32,
|
|
432
|
+
"unsigned int": 32,
|
|
433
|
+
long: 32,
|
|
434
|
+
"long int": 32,
|
|
435
|
+
"signed long": 32,
|
|
436
|
+
"signed long int": 32,
|
|
437
|
+
"unsigned long": 32,
|
|
438
|
+
"unsigned long int": 32,
|
|
439
|
+
"long long": 64,
|
|
440
|
+
"long long int": 64,
|
|
441
|
+
"signed long long": 64,
|
|
442
|
+
"signed long long int": 64,
|
|
443
|
+
"unsigned long long": 64,
|
|
444
|
+
"unsigned long long int": 64,
|
|
445
|
+
};
|
|
446
|
+
return typeWidths[typeName] ?? 0;
|
|
387
447
|
}
|
|
388
448
|
|
|
389
449
|
// Helper methods
|
|
@@ -33,6 +33,12 @@ class SymbolTable {
|
|
|
33
33
|
*/
|
|
34
34
|
private needsStructKeyword: Set<string> = new Set();
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Issue #208: Track enum backing type bit widths
|
|
38
|
+
* C++14 typed enums: enum Name : uint8_t { ... } have explicit bit widths
|
|
39
|
+
*/
|
|
40
|
+
private enumBitWidth: Map<string, number> = new Map();
|
|
41
|
+
|
|
36
42
|
/**
|
|
37
43
|
* Add a symbol to the table
|
|
38
44
|
*/
|
|
@@ -237,6 +243,42 @@ class SymbolTable {
|
|
|
237
243
|
}
|
|
238
244
|
}
|
|
239
245
|
|
|
246
|
+
/**
|
|
247
|
+
* Issue #208: Add enum bit width for a typed enum
|
|
248
|
+
* @param enumName Name of the enum (e.g., "EPressureType")
|
|
249
|
+
* @param bitWidth Bit width from backing type (e.g., 8 for uint8_t)
|
|
250
|
+
*/
|
|
251
|
+
addEnumBitWidth(enumName: string, bitWidth: number): void {
|
|
252
|
+
this.enumBitWidth.set(enumName, bitWidth);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Issue #208: Get enum bit width for a typed enum
|
|
257
|
+
* @param enumName Name of the enum
|
|
258
|
+
* @returns Bit width or undefined if not a typed enum
|
|
259
|
+
*/
|
|
260
|
+
getEnumBitWidth(enumName: string): number | undefined {
|
|
261
|
+
return this.enumBitWidth.get(enumName);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Issue #208: Get all enum bit widths for cache serialization
|
|
266
|
+
* @returns Map of enum name -> bit width
|
|
267
|
+
*/
|
|
268
|
+
getAllEnumBitWidths(): Map<string, number> {
|
|
269
|
+
return this.enumBitWidth;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Issue #208: Restore enum bit widths from cache
|
|
274
|
+
* @param bitWidths Map of enum name -> bit width
|
|
275
|
+
*/
|
|
276
|
+
restoreEnumBitWidths(bitWidths: Map<string, number>): void {
|
|
277
|
+
for (const [enumName, width] of bitWidths) {
|
|
278
|
+
this.enumBitWidth.set(enumName, width);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
240
282
|
/**
|
|
241
283
|
* Get all fields for a struct
|
|
242
284
|
* @param structName Name of the struct
|
|
@@ -301,6 +343,8 @@ class SymbolTable {
|
|
|
301
343
|
this.symbols.clear();
|
|
302
344
|
this.byFile.clear();
|
|
303
345
|
this.structFields.clear();
|
|
346
|
+
this.needsStructKeyword.clear();
|
|
347
|
+
this.enumBitWidth.clear();
|
|
304
348
|
}
|
|
305
349
|
|
|
306
350
|
/**
|