c-next 0.1.0
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 +726 -0
- package/bin/cnext.js +5 -0
- package/grammar/C.g4 +1112 -0
- package/grammar/CNext.g4 +817 -0
- package/grammar/CPP14Lexer.g4 +282 -0
- package/grammar/CPP14Parser.g4 +1072 -0
- package/package.json +85 -0
- package/src/analysis/DivisionByZeroAnalyzer.ts +378 -0
- package/src/analysis/FunctionCallAnalyzer.ts +526 -0
- package/src/analysis/InitializationAnalyzer.ts +725 -0
- package/src/analysis/NullCheckAnalyzer.ts +427 -0
- package/src/analysis/types/IDivisionByZeroError.ts +25 -0
- package/src/analysis/types/IFunctionCallError.ts +17 -0
- package/src/analysis/types/IInitializationError.ts +55 -0
- package/src/analysis/types/INullCheckError.ts +25 -0
- package/src/codegen/CodeGenerator.ts +7945 -0
- package/src/codegen/CommentExtractor.ts +240 -0
- package/src/codegen/CommentFormatter.ts +155 -0
- package/src/codegen/HeaderGenerator.ts +265 -0
- package/src/codegen/TypeResolver.ts +365 -0
- package/src/codegen/types/ECommentType.ts +10 -0
- package/src/codegen/types/IComment.ts +21 -0
- package/src/codegen/types/ICommentError.ts +15 -0
- package/src/codegen/types/TOverflowBehavior.ts +6 -0
- package/src/codegen/types/TParameterInfo.ts +13 -0
- package/src/codegen/types/TTypeConstants.ts +94 -0
- package/src/codegen/types/TTypeInfo.ts +22 -0
- package/src/index.ts +518 -0
- package/src/lib/IncludeDiscovery.ts +131 -0
- package/src/lib/InputExpansion.ts +121 -0
- package/src/lib/PlatformIODetector.ts +162 -0
- package/src/lib/transpiler.ts +439 -0
- package/src/lib/types/ITranspileResult.ts +80 -0
- package/src/parser/c/grammar/C.interp +338 -0
- package/src/parser/c/grammar/C.tokens +229 -0
- package/src/parser/c/grammar/CLexer.interp +415 -0
- package/src/parser/c/grammar/CLexer.tokens +229 -0
- package/src/parser/c/grammar/CLexer.ts +750 -0
- package/src/parser/c/grammar/CListener.ts +976 -0
- package/src/parser/c/grammar/CParser.ts +9663 -0
- package/src/parser/c/grammar/CVisitor.ts +626 -0
- package/src/parser/cpp/grammar/CPP14Lexer.interp +478 -0
- package/src/parser/cpp/grammar/CPP14Lexer.tokens +264 -0
- package/src/parser/cpp/grammar/CPP14Lexer.ts +848 -0
- package/src/parser/cpp/grammar/CPP14Parser.interp +492 -0
- package/src/parser/cpp/grammar/CPP14Parser.tokens +264 -0
- package/src/parser/cpp/grammar/CPP14Parser.ts +19961 -0
- package/src/parser/cpp/grammar/CPP14ParserListener.ts +2120 -0
- package/src/parser/cpp/grammar/CPP14ParserVisitor.ts +1354 -0
- package/src/parser/grammar/CNext.interp +340 -0
- package/src/parser/grammar/CNext.tokens +214 -0
- package/src/parser/grammar/CNextLexer.interp +374 -0
- package/src/parser/grammar/CNextLexer.tokens +214 -0
- package/src/parser/grammar/CNextLexer.ts +668 -0
- package/src/parser/grammar/CNextListener.ts +1020 -0
- package/src/parser/grammar/CNextParser.ts +9239 -0
- package/src/parser/grammar/CNextVisitor.ts +654 -0
- package/src/preprocessor/Preprocessor.ts +301 -0
- package/src/preprocessor/ToolchainDetector.ts +225 -0
- package/src/preprocessor/types/IPreprocessResult.ts +39 -0
- package/src/preprocessor/types/IToolchain.ts +27 -0
- package/src/project/FileDiscovery.ts +236 -0
- package/src/project/Project.ts +425 -0
- package/src/project/types/IProjectConfig.ts +64 -0
- package/src/symbols/CNextSymbolCollector.ts +326 -0
- package/src/symbols/CSymbolCollector.ts +457 -0
- package/src/symbols/CppSymbolCollector.ts +362 -0
- package/src/symbols/SymbolTable.ts +312 -0
- package/src/symbols/types/IConflict.ts +20 -0
- package/src/types/ESourceLanguage.ts +10 -0
- package/src/types/ESymbolKind.ts +20 -0
- package/src/types/ISymbol.ts +45 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* C++ Symbol Collector
|
|
3
|
+
* Extracts symbols from C++ parse trees for the unified symbol table
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
7
|
+
|
|
8
|
+
import { CPP14Parser } from "../parser/cpp/grammar/CPP14Parser";
|
|
9
|
+
import ISymbol from "../types/ISymbol";
|
|
10
|
+
import ESymbolKind from "../types/ESymbolKind";
|
|
11
|
+
import ESourceLanguage from "../types/ESourceLanguage";
|
|
12
|
+
|
|
13
|
+
// Import context types
|
|
14
|
+
type TranslationUnitContext = ReturnType<CPP14Parser["translationUnit"]>;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Collects symbols from a C++ parse tree
|
|
18
|
+
*/
|
|
19
|
+
class CppSymbolCollector {
|
|
20
|
+
private sourceFile: string;
|
|
21
|
+
|
|
22
|
+
private symbols: ISymbol[] = [];
|
|
23
|
+
|
|
24
|
+
private currentNamespace: string | undefined;
|
|
25
|
+
|
|
26
|
+
constructor(sourceFile: string) {
|
|
27
|
+
this.sourceFile = sourceFile;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Collect all symbols from a C++ translation unit
|
|
32
|
+
*/
|
|
33
|
+
collect(tree: TranslationUnitContext): ISymbol[] {
|
|
34
|
+
this.symbols = [];
|
|
35
|
+
this.currentNamespace = undefined;
|
|
36
|
+
|
|
37
|
+
if (!tree) {
|
|
38
|
+
return this.symbols;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const declSeq = tree.declarationseq?.();
|
|
42
|
+
if (!declSeq) {
|
|
43
|
+
return this.symbols;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
for (const decl of declSeq.declaration()) {
|
|
47
|
+
this.collectDeclaration(decl);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return this.symbols;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private collectDeclaration(decl: any): void {
|
|
54
|
+
const line = decl.start?.line ?? 0;
|
|
55
|
+
|
|
56
|
+
// Function definition
|
|
57
|
+
const funcDef = decl.functionDefinition?.();
|
|
58
|
+
if (funcDef) {
|
|
59
|
+
this.collectFunctionDefinition(funcDef, line);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Namespace definition
|
|
64
|
+
const nsDef = decl.namespaceDefinition?.();
|
|
65
|
+
if (nsDef) {
|
|
66
|
+
this.collectNamespaceDefinition(nsDef, line);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Template declaration
|
|
71
|
+
const templDecl = decl.templateDeclaration?.();
|
|
72
|
+
if (templDecl) {
|
|
73
|
+
// Skip template declarations for now - complex to handle
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Block declaration (simpleDeclaration, etc.)
|
|
78
|
+
const blockDecl = decl.blockDeclaration?.();
|
|
79
|
+
if (blockDecl) {
|
|
80
|
+
this.collectBlockDeclaration(blockDecl, line);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private collectFunctionDefinition(funcDef: any, line: number): void {
|
|
85
|
+
const declarator = funcDef.declarator?.();
|
|
86
|
+
if (!declarator) return;
|
|
87
|
+
|
|
88
|
+
const name = this.extractDeclaratorName(declarator);
|
|
89
|
+
if (!name) return;
|
|
90
|
+
|
|
91
|
+
// Get return type
|
|
92
|
+
const declSpecSeq = funcDef.declSpecifierSeq?.();
|
|
93
|
+
const returnType = declSpecSeq
|
|
94
|
+
? this.extractTypeFromDeclSpecSeq(declSpecSeq)
|
|
95
|
+
: "void";
|
|
96
|
+
|
|
97
|
+
const fullName = this.currentNamespace
|
|
98
|
+
? `${this.currentNamespace}::${name}`
|
|
99
|
+
: name;
|
|
100
|
+
|
|
101
|
+
this.symbols.push({
|
|
102
|
+
name: fullName,
|
|
103
|
+
kind: ESymbolKind.Function,
|
|
104
|
+
type: returnType,
|
|
105
|
+
sourceFile: this.sourceFile,
|
|
106
|
+
sourceLine: line,
|
|
107
|
+
sourceLanguage: ESourceLanguage.Cpp,
|
|
108
|
+
isExported: true,
|
|
109
|
+
parent: this.currentNamespace,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private collectNamespaceDefinition(nsDef: any, line: number): void {
|
|
114
|
+
const identifier = nsDef.Identifier?.();
|
|
115
|
+
const originalNs = nsDef.originalNamespaceName?.();
|
|
116
|
+
|
|
117
|
+
const name = identifier?.getText() ?? originalNs?.getText();
|
|
118
|
+
if (!name) return;
|
|
119
|
+
|
|
120
|
+
this.symbols.push({
|
|
121
|
+
name,
|
|
122
|
+
kind: ESymbolKind.Namespace,
|
|
123
|
+
sourceFile: this.sourceFile,
|
|
124
|
+
sourceLine: line,
|
|
125
|
+
sourceLanguage: ESourceLanguage.Cpp,
|
|
126
|
+
isExported: true,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Process namespace body
|
|
130
|
+
const savedNamespace = this.currentNamespace;
|
|
131
|
+
this.currentNamespace = this.currentNamespace
|
|
132
|
+
? `${this.currentNamespace}::${name}`
|
|
133
|
+
: name;
|
|
134
|
+
|
|
135
|
+
const body = nsDef.declarationseq?.();
|
|
136
|
+
if (body) {
|
|
137
|
+
for (const decl of body.declaration()) {
|
|
138
|
+
this.collectDeclaration(decl);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
this.currentNamespace = savedNamespace;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private collectBlockDeclaration(blockDecl: any, line: number): void {
|
|
146
|
+
// Simple declaration (variables, typedefs, class declarations)
|
|
147
|
+
const simpleDecl = blockDecl.simpleDeclaration?.();
|
|
148
|
+
if (simpleDecl) {
|
|
149
|
+
this.collectSimpleDeclaration(simpleDecl, line);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Using declaration
|
|
153
|
+
const usingDecl = blockDecl.usingDeclaration?.();
|
|
154
|
+
if (usingDecl) {
|
|
155
|
+
// Skip using declarations for now
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Alias declaration (using X = Y)
|
|
159
|
+
const aliasDecl = blockDecl.aliasDeclaration?.();
|
|
160
|
+
if (aliasDecl) {
|
|
161
|
+
const identifier = aliasDecl.Identifier?.();
|
|
162
|
+
if (identifier) {
|
|
163
|
+
this.symbols.push({
|
|
164
|
+
name: identifier.getText(),
|
|
165
|
+
kind: ESymbolKind.Type,
|
|
166
|
+
sourceFile: this.sourceFile,
|
|
167
|
+
sourceLine: line,
|
|
168
|
+
sourceLanguage: ESourceLanguage.Cpp,
|
|
169
|
+
isExported: true,
|
|
170
|
+
parent: this.currentNamespace,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private collectSimpleDeclaration(simpleDecl: any, line: number): void {
|
|
177
|
+
const declSpecSeq = simpleDecl.declSpecifierSeq?.();
|
|
178
|
+
if (!declSpecSeq) return;
|
|
179
|
+
|
|
180
|
+
const baseType = this.extractTypeFromDeclSpecSeq(declSpecSeq);
|
|
181
|
+
|
|
182
|
+
// Check for class specifier
|
|
183
|
+
for (const spec of declSpecSeq.declSpecifier?.() ?? []) {
|
|
184
|
+
const typeSpec = spec.typeSpecifier?.();
|
|
185
|
+
if (typeSpec) {
|
|
186
|
+
const classSpec = typeSpec.classSpecifier?.();
|
|
187
|
+
if (classSpec) {
|
|
188
|
+
this.collectClassSpecifier(classSpec, line);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const enumSpec = typeSpec.enumSpecifier?.();
|
|
192
|
+
if (enumSpec) {
|
|
193
|
+
this.collectEnumSpecifier(enumSpec, line);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Collect declarators (variables, function prototypes)
|
|
199
|
+
const initDeclList = simpleDecl.initDeclaratorList?.();
|
|
200
|
+
if (initDeclList) {
|
|
201
|
+
for (const initDecl of initDeclList.initDeclarator()) {
|
|
202
|
+
const declarator = initDecl.declarator?.();
|
|
203
|
+
if (!declarator) continue;
|
|
204
|
+
|
|
205
|
+
const name = this.extractDeclaratorName(declarator);
|
|
206
|
+
if (!name) continue;
|
|
207
|
+
|
|
208
|
+
const isFunction = this.declaratorIsFunction(declarator);
|
|
209
|
+
const fullName = this.currentNamespace
|
|
210
|
+
? `${this.currentNamespace}::${name}`
|
|
211
|
+
: name;
|
|
212
|
+
|
|
213
|
+
this.symbols.push({
|
|
214
|
+
name: fullName,
|
|
215
|
+
kind: isFunction ? ESymbolKind.Function : ESymbolKind.Variable,
|
|
216
|
+
type: baseType,
|
|
217
|
+
sourceFile: this.sourceFile,
|
|
218
|
+
sourceLine: line,
|
|
219
|
+
sourceLanguage: ESourceLanguage.Cpp,
|
|
220
|
+
isExported: true,
|
|
221
|
+
isDeclaration: isFunction,
|
|
222
|
+
parent: this.currentNamespace,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
private collectClassSpecifier(classSpec: any, line: number): void {
|
|
229
|
+
const classHead = classSpec.classHead?.();
|
|
230
|
+
if (!classHead) return;
|
|
231
|
+
|
|
232
|
+
const classHeadName = classHead.classHeadName?.();
|
|
233
|
+
if (!classHeadName) return;
|
|
234
|
+
|
|
235
|
+
const className = classHeadName.className?.();
|
|
236
|
+
if (!className) return;
|
|
237
|
+
|
|
238
|
+
const identifier = className.Identifier?.();
|
|
239
|
+
const name = identifier?.getText();
|
|
240
|
+
if (!name) return;
|
|
241
|
+
|
|
242
|
+
const fullName = this.currentNamespace
|
|
243
|
+
? `${this.currentNamespace}::${name}`
|
|
244
|
+
: name;
|
|
245
|
+
|
|
246
|
+
this.symbols.push({
|
|
247
|
+
name: fullName,
|
|
248
|
+
kind: ESymbolKind.Class,
|
|
249
|
+
sourceFile: this.sourceFile,
|
|
250
|
+
sourceLine: line,
|
|
251
|
+
sourceLanguage: ESourceLanguage.Cpp,
|
|
252
|
+
isExported: true,
|
|
253
|
+
parent: this.currentNamespace,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
private collectEnumSpecifier(enumSpec: any, line: number): void {
|
|
258
|
+
const enumHead = enumSpec.enumHead?.();
|
|
259
|
+
if (!enumHead) return;
|
|
260
|
+
|
|
261
|
+
const identifier = enumHead.Identifier?.();
|
|
262
|
+
if (!identifier) return;
|
|
263
|
+
|
|
264
|
+
const name = identifier.getText();
|
|
265
|
+
const fullName = this.currentNamespace
|
|
266
|
+
? `${this.currentNamespace}::${name}`
|
|
267
|
+
: name;
|
|
268
|
+
|
|
269
|
+
this.symbols.push({
|
|
270
|
+
name: fullName,
|
|
271
|
+
kind: ESymbolKind.Enum,
|
|
272
|
+
sourceFile: this.sourceFile,
|
|
273
|
+
sourceLine: line,
|
|
274
|
+
sourceLanguage: ESourceLanguage.Cpp,
|
|
275
|
+
isExported: true,
|
|
276
|
+
parent: this.currentNamespace,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Helper methods
|
|
281
|
+
|
|
282
|
+
private extractDeclaratorName(declarator: any): string | null {
|
|
283
|
+
// Pointer declarator -> noPointerDeclarator
|
|
284
|
+
const ptrDecl = declarator.pointerDeclarator?.();
|
|
285
|
+
if (ptrDecl) {
|
|
286
|
+
const noPtr = ptrDecl.noPointerDeclarator?.();
|
|
287
|
+
if (noPtr) {
|
|
288
|
+
return this.extractNoPointerDeclaratorName(noPtr);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// No pointer declarator
|
|
293
|
+
const noPtr = declarator.noPointerDeclarator?.();
|
|
294
|
+
if (noPtr) {
|
|
295
|
+
return this.extractNoPointerDeclaratorName(noPtr);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
private extractNoPointerDeclaratorName(noPtr: any): string | null {
|
|
302
|
+
const declId = noPtr.declaratorid?.();
|
|
303
|
+
if (declId) {
|
|
304
|
+
const idExpr = declId.idExpression?.();
|
|
305
|
+
if (idExpr) {
|
|
306
|
+
const unqualId = idExpr.unqualifiedId?.();
|
|
307
|
+
if (unqualId) {
|
|
308
|
+
const identifier = unqualId.Identifier?.();
|
|
309
|
+
if (identifier) {
|
|
310
|
+
return identifier.getText();
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Recursive case
|
|
317
|
+
const innerNoPtr = noPtr.noPointerDeclarator?.();
|
|
318
|
+
if (innerNoPtr) {
|
|
319
|
+
return this.extractNoPointerDeclaratorName(innerNoPtr);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
private declaratorIsFunction(declarator: any): boolean {
|
|
326
|
+
const ptrDecl = declarator.pointerDeclarator?.();
|
|
327
|
+
if (ptrDecl) {
|
|
328
|
+
const noPtr = ptrDecl.noPointerDeclarator?.();
|
|
329
|
+
if (noPtr?.parametersAndQualifiers?.()) {
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const noPtr = declarator.noPointerDeclarator?.();
|
|
335
|
+
if (noPtr?.parametersAndQualifiers?.()) {
|
|
336
|
+
return true;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
private extractTypeFromDeclSpecSeq(declSpecSeq: any): string {
|
|
343
|
+
const parts: string[] = [];
|
|
344
|
+
|
|
345
|
+
for (const spec of declSpecSeq.declSpecifier?.() ?? []) {
|
|
346
|
+
const typeSpec = spec.typeSpecifier?.();
|
|
347
|
+
if (typeSpec) {
|
|
348
|
+
const trailingType = typeSpec.trailingTypeSpecifier?.();
|
|
349
|
+
if (trailingType) {
|
|
350
|
+
const simpleType = trailingType.simpleTypeSpecifier?.();
|
|
351
|
+
if (simpleType) {
|
|
352
|
+
parts.push(simpleType.getText());
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return parts.join(" ") || "int";
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export default CppSymbolCollector;
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified Symbol Table
|
|
3
|
+
* Stores symbols from all source languages and detects conflicts
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import ISymbol from "../types/ISymbol";
|
|
7
|
+
import ESourceLanguage from "../types/ESourceLanguage";
|
|
8
|
+
import IConflict from "./types/IConflict";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Struct field information
|
|
12
|
+
*/
|
|
13
|
+
interface IStructFieldInfo {
|
|
14
|
+
/** Field type (e.g., "uint32_t", "uint16_t") */
|
|
15
|
+
type: string;
|
|
16
|
+
/** Array dimensions if field is an array */
|
|
17
|
+
arrayDimensions?: number[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Central symbol table for cross-language interoperability
|
|
22
|
+
*
|
|
23
|
+
* Per user requirement: Symbol conflicts between C-Next and C/C++ are ERRORS.
|
|
24
|
+
* - ERROR: Same symbol defined in C-Next and C/C++
|
|
25
|
+
* - OK: Multiple `extern` declarations in C (declaration, not definition)
|
|
26
|
+
* - OK: Function overloads in C++ (different signatures)
|
|
27
|
+
*/
|
|
28
|
+
class SymbolTable {
|
|
29
|
+
/** All symbols indexed by name */
|
|
30
|
+
private symbols: Map<string, ISymbol[]> = new Map();
|
|
31
|
+
|
|
32
|
+
/** Symbols indexed by source file */
|
|
33
|
+
private byFile: Map<string, ISymbol[]> = new Map();
|
|
34
|
+
|
|
35
|
+
/** Struct field information: struct name -> (field name -> field info) */
|
|
36
|
+
private structFields: Map<string, Map<string, IStructFieldInfo>> = new Map();
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Add a symbol to the table
|
|
40
|
+
*/
|
|
41
|
+
addSymbol(symbol: ISymbol): void {
|
|
42
|
+
// Add to name index
|
|
43
|
+
const existing = this.symbols.get(symbol.name);
|
|
44
|
+
if (existing) {
|
|
45
|
+
existing.push(symbol);
|
|
46
|
+
} else {
|
|
47
|
+
this.symbols.set(symbol.name, [symbol]);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Add to file index
|
|
51
|
+
const fileSymbols = this.byFile.get(symbol.sourceFile);
|
|
52
|
+
if (fileSymbols) {
|
|
53
|
+
fileSymbols.push(symbol);
|
|
54
|
+
} else {
|
|
55
|
+
this.byFile.set(symbol.sourceFile, [symbol]);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Add multiple symbols at once
|
|
61
|
+
*/
|
|
62
|
+
addSymbols(symbols: ISymbol[]): void {
|
|
63
|
+
for (const symbol of symbols) {
|
|
64
|
+
this.addSymbol(symbol);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get a symbol by name (returns first match, or undefined)
|
|
70
|
+
*/
|
|
71
|
+
getSymbol(name: string): ISymbol | undefined {
|
|
72
|
+
const symbols = this.symbols.get(name);
|
|
73
|
+
return symbols?.[0];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get all symbols with a given name (for overload detection)
|
|
78
|
+
*/
|
|
79
|
+
getOverloads(name: string): ISymbol[] {
|
|
80
|
+
return this.symbols.get(name) ?? [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Check if a symbol exists
|
|
85
|
+
*/
|
|
86
|
+
hasSymbol(name: string): boolean {
|
|
87
|
+
return this.symbols.has(name);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Check if a symbol has conflicts
|
|
92
|
+
*/
|
|
93
|
+
hasConflict(name: string): boolean {
|
|
94
|
+
const symbols = this.symbols.get(name);
|
|
95
|
+
if (!symbols || symbols.length <= 1) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return this.detectConflict(symbols) !== null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Get all conflicts in the symbol table
|
|
104
|
+
* Per user requirement: Strict errors for cross-language conflicts
|
|
105
|
+
*/
|
|
106
|
+
getConflicts(): IConflict[] {
|
|
107
|
+
const conflicts: IConflict[] = [];
|
|
108
|
+
|
|
109
|
+
for (const [, symbols] of this.symbols) {
|
|
110
|
+
if (symbols.length <= 1) continue;
|
|
111
|
+
|
|
112
|
+
const conflict = this.detectConflict(symbols);
|
|
113
|
+
if (conflict) {
|
|
114
|
+
conflicts.push(conflict);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return conflicts;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get symbols by source file
|
|
123
|
+
*/
|
|
124
|
+
getSymbolsByFile(file: string): ISymbol[] {
|
|
125
|
+
return this.byFile.get(file) ?? [];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get symbols by source language
|
|
130
|
+
*/
|
|
131
|
+
getSymbolsByLanguage(lang: ESourceLanguage): ISymbol[] {
|
|
132
|
+
const result: ISymbol[] = [];
|
|
133
|
+
for (const symbols of this.symbols.values()) {
|
|
134
|
+
for (const symbol of symbols) {
|
|
135
|
+
if (symbol.sourceLanguage === lang) {
|
|
136
|
+
result.push(symbol);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Get all symbols
|
|
145
|
+
*/
|
|
146
|
+
getAllSymbols(): ISymbol[] {
|
|
147
|
+
const result: ISymbol[] = [];
|
|
148
|
+
for (const symbols of this.symbols.values()) {
|
|
149
|
+
result.push(...symbols);
|
|
150
|
+
}
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Add struct field information
|
|
156
|
+
* @param structName Name of the struct
|
|
157
|
+
* @param fieldName Name of the field
|
|
158
|
+
* @param fieldType Type of the field (e.g., "uint32_t")
|
|
159
|
+
* @param arrayDimensions Optional array dimensions if field is an array
|
|
160
|
+
*/
|
|
161
|
+
addStructField(
|
|
162
|
+
structName: string,
|
|
163
|
+
fieldName: string,
|
|
164
|
+
fieldType: string,
|
|
165
|
+
arrayDimensions?: number[],
|
|
166
|
+
): void {
|
|
167
|
+
let fields = this.structFields.get(structName);
|
|
168
|
+
if (!fields) {
|
|
169
|
+
fields = new Map();
|
|
170
|
+
this.structFields.set(structName, fields);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
fields.set(fieldName, {
|
|
174
|
+
type: fieldType,
|
|
175
|
+
arrayDimensions,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Get struct field type
|
|
181
|
+
* @param structName Name of the struct
|
|
182
|
+
* @param fieldName Name of the field
|
|
183
|
+
* @returns Field type or undefined if not found
|
|
184
|
+
*/
|
|
185
|
+
getStructFieldType(
|
|
186
|
+
structName: string,
|
|
187
|
+
fieldName: string,
|
|
188
|
+
): string | undefined {
|
|
189
|
+
const fields = this.structFields.get(structName);
|
|
190
|
+
return fields?.get(fieldName)?.type;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Get struct field info (type and array dimensions)
|
|
195
|
+
* @param structName Name of the struct
|
|
196
|
+
* @param fieldName Name of the field
|
|
197
|
+
* @returns Field info or undefined if not found
|
|
198
|
+
*/
|
|
199
|
+
getStructFieldInfo(
|
|
200
|
+
structName: string,
|
|
201
|
+
fieldName: string,
|
|
202
|
+
): IStructFieldInfo | undefined {
|
|
203
|
+
const fields = this.structFields.get(structName);
|
|
204
|
+
return fields?.get(fieldName);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Get all fields for a struct
|
|
209
|
+
* @param structName Name of the struct
|
|
210
|
+
* @returns Map of field names to field info, or undefined if struct not found
|
|
211
|
+
*/
|
|
212
|
+
getStructFields(
|
|
213
|
+
structName: string,
|
|
214
|
+
): Map<string, IStructFieldInfo> | undefined {
|
|
215
|
+
return this.structFields.get(structName);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Clear all symbols
|
|
220
|
+
*/
|
|
221
|
+
clear(): void {
|
|
222
|
+
this.symbols.clear();
|
|
223
|
+
this.byFile.clear();
|
|
224
|
+
this.structFields.clear();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Get symbol count
|
|
229
|
+
*/
|
|
230
|
+
get size(): number {
|
|
231
|
+
let count = 0;
|
|
232
|
+
for (const symbols of this.symbols.values()) {
|
|
233
|
+
count += symbols.length;
|
|
234
|
+
}
|
|
235
|
+
return count;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Detect if a set of symbols with the same name represents a conflict
|
|
240
|
+
*/
|
|
241
|
+
private detectConflict(symbols: ISymbol[]): IConflict | null {
|
|
242
|
+
// Filter out pure declarations (extern in C) - they don't count as definitions
|
|
243
|
+
const definitions = symbols.filter((s) => !s.isDeclaration);
|
|
244
|
+
|
|
245
|
+
if (definitions.length <= 1) {
|
|
246
|
+
// 0 or 1 definitions = no conflict
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Check for C++ function overloads (different signatures are OK)
|
|
251
|
+
const cppFunctions = definitions.filter(
|
|
252
|
+
(s) => s.sourceLanguage === ESourceLanguage.Cpp && s.signature,
|
|
253
|
+
);
|
|
254
|
+
if (cppFunctions.length === definitions.length) {
|
|
255
|
+
// All are C++ functions with signatures
|
|
256
|
+
const uniqueSignatures = new Set(cppFunctions.map((s) => s.signature));
|
|
257
|
+
if (uniqueSignatures.size === cppFunctions.length) {
|
|
258
|
+
// All signatures are unique = valid overload, no conflict
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Check for cross-language conflict (C-Next vs C or C++)
|
|
264
|
+
const cnextDefs = definitions.filter(
|
|
265
|
+
(s) => s.sourceLanguage === ESourceLanguage.CNext,
|
|
266
|
+
);
|
|
267
|
+
const cDefs = definitions.filter(
|
|
268
|
+
(s) => s.sourceLanguage === ESourceLanguage.C,
|
|
269
|
+
);
|
|
270
|
+
const cppDefs = definitions.filter(
|
|
271
|
+
(s) => s.sourceLanguage === ESourceLanguage.Cpp,
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
if (cnextDefs.length > 0 && (cDefs.length > 0 || cppDefs.length > 0)) {
|
|
275
|
+
// C-Next + C/C++ conflict = ERROR
|
|
276
|
+
const locations = definitions.map(
|
|
277
|
+
(s) =>
|
|
278
|
+
`${s.sourceLanguage.toUpperCase()} (${s.sourceFile}:${s.sourceLine})`,
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
return {
|
|
282
|
+
symbolName: definitions[0].name,
|
|
283
|
+
definitions,
|
|
284
|
+
severity: "error",
|
|
285
|
+
message: `Symbol conflict: '${definitions[0].name}' is defined in multiple languages:\n ${locations.join("\n ")}\nRename the C-Next symbol to resolve.`,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Multiple definitions in same language (excluding overloads) = ERROR
|
|
290
|
+
if (cnextDefs.length > 1) {
|
|
291
|
+
const locations = cnextDefs.map((s) => `${s.sourceFile}:${s.sourceLine}`);
|
|
292
|
+
return {
|
|
293
|
+
symbolName: definitions[0].name,
|
|
294
|
+
definitions: cnextDefs,
|
|
295
|
+
severity: "error",
|
|
296
|
+
message: `Symbol conflict: '${definitions[0].name}' is defined multiple times in C-Next:\n ${locations.join("\n ")}`,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Same symbol in C and C++ - typically OK (same symbol)
|
|
301
|
+
// But if they have different types, might be a warning
|
|
302
|
+
if (cDefs.length > 0 && cppDefs.length > 0) {
|
|
303
|
+
// For now, allow C/C++ to share symbols (common pattern)
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export default SymbolTable;
|
|
312
|
+
export type { IStructFieldInfo };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import ISymbol from "../../types/ISymbol";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Represents a symbol conflict between languages
|
|
5
|
+
*/
|
|
6
|
+
interface IConflict {
|
|
7
|
+
/** The conflicting symbol name */
|
|
8
|
+
symbolName: string;
|
|
9
|
+
|
|
10
|
+
/** All definitions of this symbol */
|
|
11
|
+
definitions: ISymbol[];
|
|
12
|
+
|
|
13
|
+
/** Conflict severity */
|
|
14
|
+
severity: "error" | "warning";
|
|
15
|
+
|
|
16
|
+
/** Human-readable message */
|
|
17
|
+
message: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default IConflict;
|