c-next 0.2.12 → 0.2.13
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 +80 -649
- package/dist/index.js +7387 -6211
- package/dist/index.js.map +4 -4
- package/grammar/C.g4 +9 -3
- package/package.json +1 -2
- package/src/__tests__/index.test.ts +1 -1
- package/src/cli/CleanCommand.ts +8 -12
- package/src/cli/Cli.ts +29 -6
- package/src/cli/Runner.ts +42 -62
- package/src/cli/__tests__/CleanCommand.test.ts +10 -10
- package/src/cli/__tests__/Cli.test.ts +59 -7
- package/src/cli/__tests__/ConfigPrinter.test.ts +12 -12
- package/src/cli/__tests__/PathNormalizer.test.ts +5 -5
- package/src/cli/__tests__/Runner.test.ts +108 -82
- package/src/cli/serve/ServeCommand.ts +1 -1
- package/src/cli/types/ICliConfig.ts +2 -2
- package/src/lib/parseWithSymbols.ts +21 -21
- package/src/transpiler/Transpiler.ts +88 -43
- package/src/transpiler/__tests__/DualCodePaths.test.ts +29 -29
- package/src/transpiler/__tests__/Transpiler.coverage.test.ts +244 -72
- package/src/transpiler/__tests__/Transpiler.test.ts +32 -72
- package/src/transpiler/__tests__/determineProjectRoot.test.ts +30 -28
- package/src/transpiler/__tests__/needsConditionalPreprocessing.test.ts +1 -1
- package/src/transpiler/data/CNextMarkerDetector.ts +34 -0
- package/src/transpiler/data/CppEntryPointScanner.ts +174 -0
- package/src/transpiler/data/FileDiscovery.ts +2 -105
- package/src/transpiler/data/InputExpansion.ts +37 -81
- package/src/transpiler/data/__tests__/CNextMarkerDetector.test.ts +62 -0
- package/src/transpiler/data/__tests__/CppEntryPointScanner.test.ts +239 -0
- package/src/transpiler/data/__tests__/FileDiscovery.test.ts +45 -191
- package/src/transpiler/data/__tests__/InputExpansion.test.ts +36 -204
- package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +2 -2
- package/src/transpiler/logic/analysis/PassByValueAnalyzer.ts +4 -5
- package/src/transpiler/logic/parser/c/grammar/C.interp +19 -1
- package/src/transpiler/logic/parser/c/grammar/C.tokens +231 -213
- package/src/transpiler/logic/parser/c/grammar/CLexer.interp +28 -1
- package/src/transpiler/logic/parser/c/grammar/CLexer.tokens +231 -213
- package/src/transpiler/logic/parser/c/grammar/CLexer.ts +654 -600
- package/src/transpiler/logic/parser/c/grammar/CParser.ts +1175 -1099
- package/src/transpiler/logic/symbols/SymbolTable.ts +19 -7
- package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +78 -0
- package/src/transpiler/logic/symbols/cnext/__tests__/TSymbolInfoAdapter.test.ts +6 -6
- package/src/transpiler/logic/symbols/cnext/adapters/TSymbolInfoAdapter.ts +28 -27
- package/src/transpiler/logic/symbols/cnext/index.ts +4 -4
- package/src/transpiler/logic/symbols/cnext/utils/SymbolNameUtils.ts +5 -5
- package/src/transpiler/output/codegen/CodeGenerator.ts +7 -1
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +15 -0
- package/src/transpiler/output/codegen/__tests__/ExpressionWalker.test.ts +3 -3
- package/src/transpiler/output/codegen/__tests__/RequireInclude.test.ts +14 -14
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
- package/src/transpiler/output/codegen/utils/QualifiedNameGenerator.ts +7 -7
- package/src/transpiler/output/codegen/utils/__tests__/QualifiedNameGenerator.test.ts +3 -3
- package/src/transpiler/output/headers/BaseHeaderGenerator.ts +10 -1
- package/src/transpiler/output/headers/HeaderGenerator.ts +3 -0
- package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +6 -2
- package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +16 -0
- package/src/transpiler/output/headers/adapters/HeaderSymbolAdapter.ts +19 -19
- package/src/transpiler/output/headers/adapters/__tests__/HeaderSymbolAdapter.test.ts +5 -5
- package/src/transpiler/state/SymbolRegistry.ts +10 -12
- package/src/transpiler/state/__tests__/SymbolRegistry.test.ts +11 -13
- package/src/transpiler/types/IPipelineFile.ts +3 -0
- package/src/transpiler/types/ITranspilerConfig.ts +2 -2
- package/src/transpiler/types/symbols/IScopeSymbol.ts +1 -1
- package/src/utils/FunctionUtils.ts +3 -3
- package/src/utils/__tests__/FunctionUtils.test.ts +6 -4
- package/src/transpiler/data/types/IDiscoveryOptions.ts +0 -15
|
@@ -157,7 +157,7 @@ class SymbolTable {
|
|
|
157
157
|
* Called automatically when adding struct symbols.
|
|
158
158
|
*/
|
|
159
159
|
private registerStructFields(struct: IStructSymbol): void {
|
|
160
|
-
const
|
|
160
|
+
const cName = SymbolNameUtils.getTranspiledCName(struct);
|
|
161
161
|
|
|
162
162
|
for (const [fieldName, fieldInfo] of struct.fields) {
|
|
163
163
|
// Convert TType to string for structFields map
|
|
@@ -169,7 +169,7 @@ class SymbolTable {
|
|
|
169
169
|
);
|
|
170
170
|
|
|
171
171
|
this.addStructField(
|
|
172
|
-
|
|
172
|
+
cName,
|
|
173
173
|
fieldName,
|
|
174
174
|
typeString,
|
|
175
175
|
numericDims && numericDims.length > 0 ? numericDims : undefined,
|
|
@@ -651,17 +651,29 @@ class SymbolTable {
|
|
|
651
651
|
(s) => s.sourceLanguage === ESourceLanguage.Cpp,
|
|
652
652
|
);
|
|
653
653
|
|
|
654
|
-
|
|
655
|
-
|
|
654
|
+
// Issue #967: Only global-scope C-Next symbols can conflict with C/C++ symbols.
|
|
655
|
+
// Scoped symbols (e.g., Touch.read) live in a namespace and don't compete
|
|
656
|
+
// with C's global symbols (e.g., POSIX read()).
|
|
657
|
+
const conflictingCnextDefs = cnextDefs.filter((s) => {
|
|
658
|
+
const tSymbol = s as TSymbol;
|
|
659
|
+
return tSymbol.scope.name === "";
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
if (
|
|
663
|
+
conflictingCnextDefs.length > 0 &&
|
|
664
|
+
(cDefs.length > 0 || cppDefs.length > 0)
|
|
665
|
+
) {
|
|
666
|
+
const conflictingDefs = [...conflictingCnextDefs, ...cDefs, ...cppDefs];
|
|
667
|
+
const locations = conflictingDefs.map(
|
|
656
668
|
(s) =>
|
|
657
669
|
`${s.sourceLanguage.toUpperCase()} (${s.sourceFile}:${s.sourceLine})`,
|
|
658
670
|
);
|
|
659
671
|
|
|
660
672
|
return {
|
|
661
|
-
symbolName:
|
|
662
|
-
definitions:
|
|
673
|
+
symbolName: conflictingDefs[0].name,
|
|
674
|
+
definitions: conflictingDefs,
|
|
663
675
|
severity: "error",
|
|
664
|
-
message: `Symbol conflict: '${
|
|
676
|
+
message: `Symbol conflict: '${conflictingDefs[0].name}' is defined in multiple languages:\n ${locations.join("\n ")}\nRename the C-Next symbol to resolve.`,
|
|
665
677
|
};
|
|
666
678
|
}
|
|
667
679
|
|
|
@@ -478,6 +478,84 @@ describe("SymbolTable", () => {
|
|
|
478
478
|
// Two globals with same name IS a conflict
|
|
479
479
|
expect(symbolTable.hasConflict("globalVar")).toBe(true);
|
|
480
480
|
});
|
|
481
|
+
|
|
482
|
+
// Issue #967: Scoped C-Next symbols live in a namespace and don't conflict
|
|
483
|
+
// with C's global symbols. Only global-scope C-Next symbols can conflict.
|
|
484
|
+
it("should NOT detect conflict for scoped C-Next method vs C function with same bare name", () => {
|
|
485
|
+
const globalScope = TestScopeUtils.createMockGlobalScope();
|
|
486
|
+
const touchScope = TestScopeUtils.createMockScope("Touch", globalScope);
|
|
487
|
+
|
|
488
|
+
// Add C-Next scoped function 'read' in scope 'Touch'
|
|
489
|
+
// This transpiles to Touch_read()
|
|
490
|
+
symbolTable.addTSymbol({
|
|
491
|
+
kind: "function",
|
|
492
|
+
name: "read",
|
|
493
|
+
sourceFile: "touch.cnx",
|
|
494
|
+
sourceLine: 28,
|
|
495
|
+
sourceLanguage: ESourceLanguage.CNext,
|
|
496
|
+
isExported: true,
|
|
497
|
+
returnType: TTypeUtils.createPrimitive("u8"),
|
|
498
|
+
parameters: [],
|
|
499
|
+
scope: touchScope,
|
|
500
|
+
visibility: "public",
|
|
501
|
+
body: null,
|
|
502
|
+
} as IFunctionSymbol);
|
|
503
|
+
|
|
504
|
+
// Add C function 'read' from POSIX headers
|
|
505
|
+
// This stays as read()
|
|
506
|
+
symbolTable.addCSymbol({
|
|
507
|
+
kind: "function",
|
|
508
|
+
name: "read",
|
|
509
|
+
sourceFile: "lv_pthread.h",
|
|
510
|
+
sourceLine: 80,
|
|
511
|
+
sourceLanguage: ESourceLanguage.C,
|
|
512
|
+
isExported: true,
|
|
513
|
+
type: "ssize_t",
|
|
514
|
+
parameters: [
|
|
515
|
+
{ name: "fd", type: "int", isConst: false, isArray: false },
|
|
516
|
+
{ name: "buf", type: "void*", isConst: false, isArray: false },
|
|
517
|
+
{ name: "count", type: "size_t", isConst: false, isArray: false },
|
|
518
|
+
],
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
// Touch.read() is in a namespace — does NOT conflict with C's global read()
|
|
522
|
+
expect(symbolTable.hasConflict("read")).toBe(false);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
// Issue #967: Global C-Next functions SHOULD still conflict with C functions
|
|
526
|
+
it("should detect conflict for global C-Next function vs C function", () => {
|
|
527
|
+
const globalScope = TestScopeUtils.createMockGlobalScope();
|
|
528
|
+
|
|
529
|
+
// Add global C-Next function 'read'
|
|
530
|
+
symbolTable.addTSymbol({
|
|
531
|
+
kind: "function",
|
|
532
|
+
name: "read",
|
|
533
|
+
sourceFile: "utils.cnx",
|
|
534
|
+
sourceLine: 5,
|
|
535
|
+
sourceLanguage: ESourceLanguage.CNext,
|
|
536
|
+
isExported: true,
|
|
537
|
+
returnType: TTypeUtils.createPrimitive("u8"),
|
|
538
|
+
parameters: [],
|
|
539
|
+
scope: globalScope,
|
|
540
|
+
visibility: "public",
|
|
541
|
+
body: null,
|
|
542
|
+
} as IFunctionSymbol);
|
|
543
|
+
|
|
544
|
+
// Add C function 'read'
|
|
545
|
+
symbolTable.addCSymbol({
|
|
546
|
+
kind: "function",
|
|
547
|
+
name: "read",
|
|
548
|
+
sourceFile: "unistd.h",
|
|
549
|
+
sourceLine: 100,
|
|
550
|
+
sourceLanguage: ESourceLanguage.C,
|
|
551
|
+
isExported: true,
|
|
552
|
+
type: "ssize_t",
|
|
553
|
+
parameters: [],
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
// Global C-Next read() DOES conflict with C's read()
|
|
557
|
+
expect(symbolTable.hasConflict("read")).toBe(true);
|
|
558
|
+
});
|
|
481
559
|
});
|
|
482
560
|
|
|
483
561
|
// ========================================================================
|
|
@@ -466,7 +466,7 @@ describe("TSymbolInfoAdapter", () => {
|
|
|
466
466
|
const motorScope = TestScopeUtils.createMockScope("Motor");
|
|
467
467
|
const variable: IVariableSymbol = {
|
|
468
468
|
kind: "variable",
|
|
469
|
-
name: "MAX_SPEED", // Bare name - adapter computes
|
|
469
|
+
name: "MAX_SPEED", // Bare name - adapter computes transpiled C name
|
|
470
470
|
scope: motorScope,
|
|
471
471
|
sourceFile: "test.cnx",
|
|
472
472
|
sourceLine: 1,
|
|
@@ -481,7 +481,7 @@ describe("TSymbolInfoAdapter", () => {
|
|
|
481
481
|
|
|
482
482
|
const info = TSymbolInfoAdapter.convert([variable]);
|
|
483
483
|
|
|
484
|
-
// Adapter stores using
|
|
484
|
+
// Adapter stores using transpiled C name (scope + bare name)
|
|
485
485
|
expect(info.scopePrivateConstValues.get("Motor_MAX_SPEED")).toBe("255");
|
|
486
486
|
});
|
|
487
487
|
|
|
@@ -489,7 +489,7 @@ describe("TSymbolInfoAdapter", () => {
|
|
|
489
489
|
const motorScope = TestScopeUtils.createMockScope("Motor");
|
|
490
490
|
const variable: IVariableSymbol = {
|
|
491
491
|
kind: "variable",
|
|
492
|
-
name: "PUBLIC_CONST", // Bare name - adapter computes
|
|
492
|
+
name: "PUBLIC_CONST", // Bare name - adapter computes transpiled C name
|
|
493
493
|
scope: motorScope,
|
|
494
494
|
sourceFile: "test.cnx",
|
|
495
495
|
sourceLine: 1,
|
|
@@ -513,7 +513,7 @@ describe("TSymbolInfoAdapter", () => {
|
|
|
513
513
|
const motorScope = TestScopeUtils.createMockScope("Motor");
|
|
514
514
|
const variable: IVariableSymbol = {
|
|
515
515
|
kind: "variable",
|
|
516
|
-
name: "counter", // Bare name - adapter computes
|
|
516
|
+
name: "counter", // Bare name - adapter computes transpiled C name
|
|
517
517
|
scope: motorScope,
|
|
518
518
|
sourceFile: "test.cnx",
|
|
519
519
|
sourceLine: 1,
|
|
@@ -535,7 +535,7 @@ describe("TSymbolInfoAdapter", () => {
|
|
|
535
535
|
const motorScope = TestScopeUtils.createMockScope("Motor");
|
|
536
536
|
const variable: IVariableSymbol = {
|
|
537
537
|
kind: "variable",
|
|
538
|
-
name: "LOOKUP_TABLE", // Bare name - adapter computes
|
|
538
|
+
name: "LOOKUP_TABLE", // Bare name - adapter computes transpiled C name
|
|
539
539
|
scope: motorScope,
|
|
540
540
|
sourceFile: "test.cnx",
|
|
541
541
|
sourceLine: 1,
|
|
@@ -562,7 +562,7 @@ describe("TSymbolInfoAdapter", () => {
|
|
|
562
562
|
const motorScope = TestScopeUtils.createMockScope("Motor");
|
|
563
563
|
const variable: IVariableSymbol = {
|
|
564
564
|
kind: "variable",
|
|
565
|
-
name: "MATRIX", // Bare name - adapter computes
|
|
565
|
+
name: "MATRIX", // Bare name - adapter computes transpiled C name
|
|
566
566
|
scope: motorScope,
|
|
567
567
|
sourceFile: "test.cnx",
|
|
568
568
|
sourceLine: 1,
|
|
@@ -239,8 +239,9 @@ class TSymbolInfoAdapter {
|
|
|
239
239
|
|
|
240
240
|
// === Private Processing Methods ===
|
|
241
241
|
|
|
242
|
-
// Use shared utility for
|
|
243
|
-
private static readonly
|
|
242
|
+
// Use shared utility for transpiled C names
|
|
243
|
+
private static readonly getTranspiledCName =
|
|
244
|
+
SymbolNameUtils.getTranspiledCName;
|
|
244
245
|
|
|
245
246
|
private static processStruct(
|
|
246
247
|
struct: IStructSymbol,
|
|
@@ -249,9 +250,9 @@ class TSymbolInfoAdapter {
|
|
|
249
250
|
structFieldArrays: Map<string, Set<string>>,
|
|
250
251
|
structFieldDimensions: Map<string, Map<string, number[]>>,
|
|
251
252
|
): void {
|
|
252
|
-
// Use
|
|
253
|
-
const
|
|
254
|
-
knownStructs.add(
|
|
253
|
+
// Use transpiled C name for lookups (e.g., "Geometry_Point")
|
|
254
|
+
const cName = TSymbolInfoAdapter.getTranspiledCName(struct);
|
|
255
|
+
knownStructs.add(cName);
|
|
255
256
|
|
|
256
257
|
const fields = new Map<string, string>();
|
|
257
258
|
const arrayFields = new Set<string>();
|
|
@@ -277,10 +278,10 @@ class TSymbolInfoAdapter {
|
|
|
277
278
|
}
|
|
278
279
|
}
|
|
279
280
|
|
|
280
|
-
structFields.set(
|
|
281
|
-
structFieldArrays.set(
|
|
281
|
+
structFields.set(cName, fields);
|
|
282
|
+
structFieldArrays.set(cName, arrayFields);
|
|
282
283
|
if (dimensions.size > 0) {
|
|
283
|
-
structFieldDimensions.set(
|
|
284
|
+
structFieldDimensions.set(cName, dimensions);
|
|
284
285
|
}
|
|
285
286
|
}
|
|
286
287
|
|
|
@@ -289,9 +290,9 @@ class TSymbolInfoAdapter {
|
|
|
289
290
|
knownEnums: Set<string>,
|
|
290
291
|
enumMembers: Map<string, Map<string, number>>,
|
|
291
292
|
): void {
|
|
292
|
-
const
|
|
293
|
-
knownEnums.add(
|
|
294
|
-
enumMembers.set(
|
|
293
|
+
const cName = TSymbolInfoAdapter.getTranspiledCName(enumSym);
|
|
294
|
+
knownEnums.add(cName);
|
|
295
|
+
enumMembers.set(cName, new Map(enumSym.members));
|
|
295
296
|
}
|
|
296
297
|
|
|
297
298
|
private static processBitmap(
|
|
@@ -301,10 +302,10 @@ class TSymbolInfoAdapter {
|
|
|
301
302
|
bitmapBackingType: Map<string, string>,
|
|
302
303
|
bitmapBitWidth: Map<string, number>,
|
|
303
304
|
): void {
|
|
304
|
-
const
|
|
305
|
-
knownBitmaps.add(
|
|
306
|
-
bitmapBackingType.set(
|
|
307
|
-
bitmapBitWidth.set(
|
|
305
|
+
const cName = TSymbolInfoAdapter.getTranspiledCName(bitmap);
|
|
306
|
+
knownBitmaps.add(cName);
|
|
307
|
+
bitmapBackingType.set(cName, bitmap.backingType);
|
|
308
|
+
bitmapBitWidth.set(cName, bitmap.bitWidth);
|
|
308
309
|
|
|
309
310
|
const fields = new Map<string, { offset: number; width: number }>();
|
|
310
311
|
for (const [fieldName, fieldInfo] of bitmap.fields) {
|
|
@@ -313,7 +314,7 @@ class TSymbolInfoAdapter {
|
|
|
313
314
|
width: fieldInfo.width,
|
|
314
315
|
});
|
|
315
316
|
}
|
|
316
|
-
bitmapFields.set(
|
|
317
|
+
bitmapFields.set(cName, fields);
|
|
317
318
|
}
|
|
318
319
|
|
|
319
320
|
private static processScope(
|
|
@@ -338,18 +339,18 @@ class TSymbolInfoAdapter {
|
|
|
338
339
|
knownBitmaps: Set<string>,
|
|
339
340
|
maps: IRegisterMaps,
|
|
340
341
|
): void {
|
|
341
|
-
const
|
|
342
|
-
maps.knownRegisters.add(
|
|
343
|
-
maps.registerBaseAddresses.set(
|
|
342
|
+
const cName = TSymbolInfoAdapter.getTranspiledCName(register);
|
|
343
|
+
maps.knownRegisters.add(cName);
|
|
344
|
+
maps.registerBaseAddresses.set(cName, register.baseAddress);
|
|
344
345
|
|
|
345
346
|
// Check if this is a scoped register (has non-global scope)
|
|
346
347
|
const isScoped = register.scope.name !== "";
|
|
347
348
|
if (isScoped) {
|
|
348
|
-
maps.scopedRegisters.set(
|
|
349
|
+
maps.scopedRegisters.set(cName, register.baseAddress);
|
|
349
350
|
}
|
|
350
351
|
|
|
351
352
|
for (const [memberName, memberInfo] of register.members) {
|
|
352
|
-
const fullName = `${
|
|
353
|
+
const fullName = `${cName}_${memberName}`;
|
|
353
354
|
|
|
354
355
|
maps.registerMemberAccess.set(fullName, memberInfo.access);
|
|
355
356
|
maps.registerMemberOffsets.set(fullName, memberInfo.offset);
|
|
@@ -370,7 +371,7 @@ class TSymbolInfoAdapter {
|
|
|
370
371
|
scopeMembers: Map<string, Set<string>>,
|
|
371
372
|
scopePrivateConstValues: Map<string, string>,
|
|
372
373
|
): void {
|
|
373
|
-
const
|
|
374
|
+
const cName = TSymbolInfoAdapter.getTranspiledCName(variable);
|
|
374
375
|
const scopeName = variable.scope.name;
|
|
375
376
|
const isScoped = scopeName !== "";
|
|
376
377
|
|
|
@@ -381,7 +382,7 @@ class TSymbolInfoAdapter {
|
|
|
381
382
|
members = new Set<string>();
|
|
382
383
|
scopeMembers.set(scopeName, members);
|
|
383
384
|
}
|
|
384
|
-
members.add(variable.name); // Add local name (e.g., "value"), not
|
|
385
|
+
members.add(variable.name); // Add local name (e.g., "value"), not transpiled C name
|
|
385
386
|
}
|
|
386
387
|
|
|
387
388
|
// Issue #282: Track private const values for inlining
|
|
@@ -395,7 +396,7 @@ class TSymbolInfoAdapter {
|
|
|
395
396
|
variable.initialValue &&
|
|
396
397
|
!variable.isArray
|
|
397
398
|
) {
|
|
398
|
-
scopePrivateConstValues.set(
|
|
399
|
+
scopePrivateConstValues.set(cName, variable.initialValue);
|
|
399
400
|
}
|
|
400
401
|
}
|
|
401
402
|
|
|
@@ -405,10 +406,10 @@ class TSymbolInfoAdapter {
|
|
|
405
406
|
): void {
|
|
406
407
|
// Track function return types for enum validation in assignments
|
|
407
408
|
// This enables recognizing that Motor.getMode() returns Motor_EMode
|
|
408
|
-
// Use
|
|
409
|
-
const
|
|
409
|
+
// Use transpiled C name (e.g., "Motor_getMode") for lookup consistency
|
|
410
|
+
const cName = TSymbolInfoAdapter.getTranspiledCName(func);
|
|
410
411
|
const returnTypeStr = TypeResolver.getTypeName(func.returnType);
|
|
411
|
-
functionReturnTypes.set(
|
|
412
|
+
functionReturnTypes.set(cName, returnTypeStr);
|
|
412
413
|
}
|
|
413
414
|
|
|
414
415
|
private static cnextTypeToCType(typeName: string): string {
|
|
@@ -164,7 +164,7 @@ class CNextResolver {
|
|
|
164
164
|
globalScope,
|
|
165
165
|
);
|
|
166
166
|
symbols.push(symbol);
|
|
167
|
-
// Use
|
|
167
|
+
// Use transpiled C name (global bitmaps have no scope prefix)
|
|
168
168
|
knownBitmaps.add(symbol.name);
|
|
169
169
|
}
|
|
170
170
|
|
|
@@ -200,9 +200,9 @@ class CNextResolver {
|
|
|
200
200
|
const bitmapCtx = member.bitmapDeclaration()!;
|
|
201
201
|
const symbol = BitmapCollector.collect(bitmapCtx, sourceFile, scope);
|
|
202
202
|
symbols.push(symbol);
|
|
203
|
-
// Use
|
|
204
|
-
const
|
|
205
|
-
knownBitmaps.add(
|
|
203
|
+
// Use transpiled C name (e.g., "Timer_ControlBits") for scoped bitmaps
|
|
204
|
+
const cName = `${scopeName}_${symbol.name}`;
|
|
205
|
+
knownBitmaps.add(cName);
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
// Collect structs early so they're available as types
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Get the C
|
|
7
|
-
*
|
|
6
|
+
* Get the transpiled C name for a symbol (e.g., "Geometry_Point" for Point in Geometry scope).
|
|
7
|
+
* Use only at the output layer for C code generation, not for input-side logic.
|
|
8
8
|
*
|
|
9
9
|
* @param symbol Object with name and scope.name properties
|
|
10
|
-
* @returns The
|
|
10
|
+
* @returns The C output name (e.g., "Motor_init") or bare name if global scope
|
|
11
11
|
*/
|
|
12
|
-
function
|
|
12
|
+
function getTranspiledCName(symbol: {
|
|
13
13
|
name: string;
|
|
14
14
|
scope: { name: string };
|
|
15
15
|
}): string {
|
|
@@ -21,7 +21,7 @@ function getMangledName(symbol: {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
class SymbolNameUtils {
|
|
24
|
-
static readonly
|
|
24
|
+
static readonly getTranspiledCName = getTranspiledCName;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export default SymbolNameUtils;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Transforms C-Next AST to clean, readable C code
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { basename } from "node:path";
|
|
6
7
|
import { CommonTokenStream, ParserRuleContext } from "antlr4ng";
|
|
7
8
|
import * as Parser from "../../logic/parser/grammar/CNextParser";
|
|
8
9
|
|
|
@@ -2225,9 +2226,14 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
2225
2226
|
const output: string[] = [];
|
|
2226
2227
|
const symbols = CodeGenState.symbols!;
|
|
2227
2228
|
// Add header comment
|
|
2229
|
+
const sourcePath = CodeGenState.sourcePath;
|
|
2230
|
+
const generatedLine = sourcePath
|
|
2231
|
+
? ` * Generated by C-Next Transpiler from: ${basename(sourcePath)}`
|
|
2232
|
+
: " * Generated by C-Next Transpiler";
|
|
2233
|
+
|
|
2228
2234
|
output.push(
|
|
2229
2235
|
"/**",
|
|
2230
|
-
|
|
2236
|
+
generatedLine,
|
|
2231
2237
|
" * A safer C for embedded systems",
|
|
2232
2238
|
" */",
|
|
2233
2239
|
"",
|
|
@@ -164,6 +164,21 @@ describe("CodeGenerator", () => {
|
|
|
164
164
|
|
|
165
165
|
expect(code).toContain("void foo");
|
|
166
166
|
});
|
|
167
|
+
|
|
168
|
+
it("should include source path in generation comment", () => {
|
|
169
|
+
const source = `void test() { }`;
|
|
170
|
+
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
171
|
+
const generator = new CodeGenerator();
|
|
172
|
+
const tSymbols = CNextResolver.resolve(tree, "test.cnx");
|
|
173
|
+
const symbols = TSymbolInfoAdapter.convert(tSymbols);
|
|
174
|
+
|
|
175
|
+
const code = generator.generate(tree, tokenStream, {
|
|
176
|
+
symbolInfo: symbols,
|
|
177
|
+
sourcePath: "led.cnx",
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
expect(code).toContain("Generated by C-Next Transpiler from: led.cnx");
|
|
181
|
+
});
|
|
167
182
|
});
|
|
168
183
|
|
|
169
184
|
describe("IOrchestrator interface", () => {
|
|
@@ -311,7 +311,7 @@ describe("ExpressionWalker - const inference integration", () => {
|
|
|
311
311
|
caller(val);
|
|
312
312
|
}
|
|
313
313
|
`;
|
|
314
|
-
const transpiler = new Transpiler({
|
|
314
|
+
const transpiler = new Transpiler({ input: "" });
|
|
315
315
|
const transpileResult = (
|
|
316
316
|
await transpiler.transpile({ kind: "source", source: source })
|
|
317
317
|
).files[0];
|
|
@@ -341,7 +341,7 @@ describe("ExpressionWalker - const inference integration", () => {
|
|
|
341
341
|
caller(val);
|
|
342
342
|
}
|
|
343
343
|
`;
|
|
344
|
-
const transpiler = new Transpiler({
|
|
344
|
+
const transpiler = new Transpiler({ input: "" });
|
|
345
345
|
const transpileResult = (
|
|
346
346
|
await transpiler.transpile({ kind: "source", source: source })
|
|
347
347
|
).files[0];
|
|
@@ -370,7 +370,7 @@ describe("ExpressionWalker - const inference integration", () => {
|
|
|
370
370
|
caller(val);
|
|
371
371
|
}
|
|
372
372
|
`;
|
|
373
|
-
const transpiler = new Transpiler({
|
|
373
|
+
const transpiler = new Transpiler({ input: "" });
|
|
374
374
|
const transpileResult = (
|
|
375
375
|
await transpiler.transpile({ kind: "source", source: source })
|
|
376
376
|
).files[0];
|
|
@@ -18,7 +18,7 @@ describe("CodeGenerator requireInclude", () => {
|
|
|
18
18
|
|
|
19
19
|
describe("stdint includes", () => {
|
|
20
20
|
it("includes stdint.h for u8 type", async () => {
|
|
21
|
-
const transpiler = new Transpiler({
|
|
21
|
+
const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
|
|
22
22
|
|
|
23
23
|
const result = (
|
|
24
24
|
await transpiler.transpile({ kind: "source", source: "u8 value <- 0;" })
|
|
@@ -29,7 +29,7 @@ describe("CodeGenerator requireInclude", () => {
|
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
it("includes stdint.h for u16 type", async () => {
|
|
32
|
-
const transpiler = new Transpiler({
|
|
32
|
+
const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
|
|
33
33
|
|
|
34
34
|
const result = (
|
|
35
35
|
await transpiler.transpile({
|
|
@@ -43,7 +43,7 @@ describe("CodeGenerator requireInclude", () => {
|
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
it("includes stdint.h for u32 type", async () => {
|
|
46
|
-
const transpiler = new Transpiler({
|
|
46
|
+
const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
|
|
47
47
|
|
|
48
48
|
const result = (
|
|
49
49
|
await transpiler.transpile({
|
|
@@ -57,7 +57,7 @@ describe("CodeGenerator requireInclude", () => {
|
|
|
57
57
|
});
|
|
58
58
|
|
|
59
59
|
it("includes stdint.h for i32 type", async () => {
|
|
60
|
-
const transpiler = new Transpiler({
|
|
60
|
+
const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
|
|
61
61
|
|
|
62
62
|
const result = (
|
|
63
63
|
await transpiler.transpile({
|
|
@@ -71,7 +71,7 @@ describe("CodeGenerator requireInclude", () => {
|
|
|
71
71
|
});
|
|
72
72
|
|
|
73
73
|
it("includes stdint.h for bitmap types", async () => {
|
|
74
|
-
const transpiler = new Transpiler({
|
|
74
|
+
const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
|
|
75
75
|
|
|
76
76
|
const result = (
|
|
77
77
|
await transpiler.transpile({
|
|
@@ -94,7 +94,7 @@ describe("CodeGenerator requireInclude", () => {
|
|
|
94
94
|
|
|
95
95
|
describe("stdbool includes", () => {
|
|
96
96
|
it("includes stdbool.h for bool type", async () => {
|
|
97
|
-
const transpiler = new Transpiler({
|
|
97
|
+
const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
|
|
98
98
|
|
|
99
99
|
const result = (
|
|
100
100
|
await transpiler.transpile({
|
|
@@ -110,7 +110,7 @@ describe("CodeGenerator requireInclude", () => {
|
|
|
110
110
|
|
|
111
111
|
describe("string includes", () => {
|
|
112
112
|
it("includes string.h for bounded string type", async () => {
|
|
113
|
-
const transpiler = new Transpiler({
|
|
113
|
+
const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
|
|
114
114
|
|
|
115
115
|
const result = (
|
|
116
116
|
await transpiler.transpile({
|
|
@@ -124,7 +124,7 @@ describe("CodeGenerator requireInclude", () => {
|
|
|
124
124
|
});
|
|
125
125
|
|
|
126
126
|
it("includes string.h for const string inference", async () => {
|
|
127
|
-
const transpiler = new Transpiler({
|
|
127
|
+
const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
|
|
128
128
|
|
|
129
129
|
const result = (
|
|
130
130
|
await transpiler.transpile({
|
|
@@ -140,7 +140,7 @@ describe("CodeGenerator requireInclude", () => {
|
|
|
140
140
|
|
|
141
141
|
describe("isr includes", () => {
|
|
142
142
|
it("generates ISR typedef for ISR type", async () => {
|
|
143
|
-
const transpiler = new Transpiler({
|
|
143
|
+
const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
|
|
144
144
|
|
|
145
145
|
const result = (
|
|
146
146
|
await transpiler.transpile({
|
|
@@ -156,7 +156,7 @@ describe("CodeGenerator requireInclude", () => {
|
|
|
156
156
|
|
|
157
157
|
describe("float static assert includes", () => {
|
|
158
158
|
it("generates static assert for float bit indexing write", async () => {
|
|
159
|
-
const transpiler = new Transpiler({
|
|
159
|
+
const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
|
|
160
160
|
|
|
161
161
|
const result = (
|
|
162
162
|
await transpiler.transpile({
|
|
@@ -177,7 +177,7 @@ describe("CodeGenerator requireInclude", () => {
|
|
|
177
177
|
});
|
|
178
178
|
|
|
179
179
|
it("generates static assert for float bit indexing read (no string.h)", async () => {
|
|
180
|
-
const transpiler = new Transpiler({
|
|
180
|
+
const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
|
|
181
181
|
|
|
182
182
|
const result = (
|
|
183
183
|
await transpiler.transpile({
|
|
@@ -201,7 +201,7 @@ describe("CodeGenerator requireInclude", () => {
|
|
|
201
201
|
|
|
202
202
|
describe("limits includes", () => {
|
|
203
203
|
it("includes limits.h for float-to-int clamp cast", async () => {
|
|
204
|
-
const transpiler = new Transpiler({
|
|
204
|
+
const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
|
|
205
205
|
|
|
206
206
|
const result = (
|
|
207
207
|
await transpiler.transpile({
|
|
@@ -221,7 +221,7 @@ describe("CodeGenerator requireInclude", () => {
|
|
|
221
221
|
|
|
222
222
|
describe("multiple includes", () => {
|
|
223
223
|
it("includes multiple headers when needed", async () => {
|
|
224
|
-
const transpiler = new Transpiler({
|
|
224
|
+
const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
|
|
225
225
|
|
|
226
226
|
const result = (
|
|
227
227
|
await transpiler.transpile({
|
|
@@ -241,7 +241,7 @@ describe("CodeGenerator requireInclude", () => {
|
|
|
241
241
|
});
|
|
242
242
|
|
|
243
243
|
it("does not include unused headers", async () => {
|
|
244
|
-
const transpiler = new Transpiler({
|
|
244
|
+
const transpiler = new Transpiler({ input: "", noCache: true }, mockFs);
|
|
245
245
|
|
|
246
246
|
const result = (
|
|
247
247
|
await transpiler.transpile({
|
|
@@ -11,7 +11,7 @@ import Transpiler from "../../../Transpiler";
|
|
|
11
11
|
* Helper to transpile C-Next source and return the C output
|
|
12
12
|
*/
|
|
13
13
|
async function transpileSource(source: string): Promise<string> {
|
|
14
|
-
const transpiler = new Transpiler({
|
|
14
|
+
const transpiler = new Transpiler({ input: "" });
|
|
15
15
|
const result = (
|
|
16
16
|
await transpiler.transpile({ kind: "source", source: source })
|
|
17
17
|
).files[0];
|
|
@@ -130,7 +130,7 @@ describe("trackVariableTypeWithName helpers", () => {
|
|
|
130
130
|
}
|
|
131
131
|
`;
|
|
132
132
|
const code = await transpileSource(source);
|
|
133
|
-
// Scoped type should be
|
|
133
|
+
// Scoped type should be transpiled to Motor_State
|
|
134
134
|
expect(code).toContain("Motor_State");
|
|
135
135
|
});
|
|
136
136
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* QualifiedNameGenerator - C-style name generation for C-Next symbols
|
|
3
3
|
*
|
|
4
|
-
* Provides C
|
|
5
|
-
* Delegates to FunctionUtils.
|
|
4
|
+
* Provides transpiled C name generation for use in the output layer.
|
|
5
|
+
* Delegates to FunctionUtils.getTranspiledCName() for the actual implementation
|
|
6
6
|
* to avoid duplication with the types layer.
|
|
7
7
|
*
|
|
8
8
|
* Design decisions:
|
|
@@ -24,16 +24,16 @@ class QualifiedNameGenerator {
|
|
|
24
24
|
// ============================================================================
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
* Generate the C
|
|
27
|
+
* Generate the transpiled C name for a function.
|
|
28
28
|
*
|
|
29
29
|
* For global scope functions, returns the bare name (e.g., "main").
|
|
30
30
|
* For scoped functions, returns "Scope_name" (e.g., "Test_fillData").
|
|
31
31
|
* For nested scopes, returns "Outer_Inner_name" (e.g., "Outer_Inner_deepFunc").
|
|
32
32
|
*
|
|
33
|
-
* Delegates to FunctionUtils.
|
|
33
|
+
* Delegates to FunctionUtils.getTranspiledCName() to avoid duplication.
|
|
34
34
|
*/
|
|
35
35
|
static forFunction(func: IFunctionSymbol): string {
|
|
36
|
-
return FunctionUtils.
|
|
36
|
+
return FunctionUtils.getTranspiledCName(func);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/**
|
|
@@ -61,7 +61,7 @@ class QualifiedNameGenerator {
|
|
|
61
61
|
*
|
|
62
62
|
* @param scopeName Scope name (e.g., "Test", "Outer.Inner") or undefined for global
|
|
63
63
|
* @param funcName Bare function name (e.g., "fillData")
|
|
64
|
-
* @returns C
|
|
64
|
+
* @returns Transpiled C name (e.g., "Test_fillData")
|
|
65
65
|
*/
|
|
66
66
|
static forFunctionStrings(
|
|
67
67
|
scopeName: string | undefined,
|
|
@@ -100,7 +100,7 @@ class QualifiedNameGenerator {
|
|
|
100
100
|
*
|
|
101
101
|
* @param scopeName Scope name or undefined for global
|
|
102
102
|
* @param memberName Member name
|
|
103
|
-
* @returns C
|
|
103
|
+
* @returns Transpiled C name
|
|
104
104
|
*/
|
|
105
105
|
static forMember(scopeName: string | undefined, memberName: string): string {
|
|
106
106
|
if (!scopeName) {
|