c-next 0.2.11 → 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 +8273 -6357
- package/dist/index.js.map +4 -4
- package/grammar/C.g4 +17 -4
- package/package.json +3 -3
- 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 +99 -46
- 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 +33 -3
- package/src/transpiler/logic/parser/c/grammar/C.tokens +237 -207
- package/src/transpiler/logic/parser/c/grammar/CLexer.interp +48 -3
- package/src/transpiler/logic/parser/c/grammar/CLexer.tokens +237 -207
- package/src/transpiler/logic/parser/c/grammar/CLexer.ts +702 -611
- package/src/transpiler/logic/parser/c/grammar/CParser.ts +1221 -1107
- package/src/transpiler/logic/symbols/SymbolTable.ts +147 -73
- package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +157 -14
- package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +3 -3
- package/src/transpiler/logic/symbols/c/collectors/StructCollector.ts +7 -37
- 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 +16 -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/helpers/FunctionContextManager.ts +15 -3
- package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +14 -6
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +1 -0
- package/src/transpiler/output/codegen/types/IFunctionContextCallbacks.ts +2 -0
- 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/ICachedFileEntry.ts +4 -0
- 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/utils/cache/CacheManager.ts +28 -15
- package/src/utils/cache/__tests__/CacheManager.test.ts +6 -4
- package/src/transpiler/data/types/IDiscoveryOptions.ts +0 -15
|
@@ -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
|
"",
|
|
@@ -3675,6 +3681,8 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
3675
3681
|
isStructType: (typeName: string) => this.isStructType(typeName),
|
|
3676
3682
|
resolveQualifiedType: (identifiers: string[]) =>
|
|
3677
3683
|
this.resolveQualifiedType(identifiers),
|
|
3684
|
+
isTypedefStructType: (t: string) =>
|
|
3685
|
+
CodeGenState.symbolTable?.isTypedefStructType(t) ?? false,
|
|
3678
3686
|
};
|
|
3679
3687
|
}
|
|
3680
3688
|
|
|
@@ -3766,6 +3774,8 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
3766
3774
|
isCallbackCompatible,
|
|
3767
3775
|
forcePassByReference,
|
|
3768
3776
|
forceConst,
|
|
3777
|
+
isTypedefStructType: (t) =>
|
|
3778
|
+
CodeGenState.symbolTable?.isTypedefStructType(t) ?? false,
|
|
3769
3779
|
});
|
|
3770
3780
|
|
|
3771
3781
|
// Use shared builder with C/C++ mode
|
|
@@ -3892,6 +3902,11 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
3892
3902
|
): string {
|
|
3893
3903
|
const type = this.generateType(ctx.type());
|
|
3894
3904
|
|
|
3905
|
+
// Issue #958: C-header typedef struct types always need pointer semantics
|
|
3906
|
+
if (CodeGenState.symbolTable?.isTypedefStructType(type)) {
|
|
3907
|
+
return `${type}*`;
|
|
3908
|
+
}
|
|
3909
|
+
|
|
3895
3910
|
if (!ctx.expression()) {
|
|
3896
3911
|
return type;
|
|
3897
3912
|
}
|
|
@@ -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
|
|
|
@@ -160,19 +160,27 @@ class FunctionContextManager {
|
|
|
160
160
|
const isCallbackPointerParam =
|
|
161
161
|
callbackTypedefInfo?.shouldBePointer ?? false;
|
|
162
162
|
|
|
163
|
+
// Issue #958: Check if type is a typedef'd struct from C headers
|
|
164
|
+
const isTypedefStruct =
|
|
165
|
+
callbacks.isTypedefStructType?.(typeInfo.typeName) ?? false;
|
|
166
|
+
|
|
163
167
|
// Determine isStruct: for callback-compatible params, both typedef AND type info matter
|
|
164
168
|
// - If typedef says pointer AND it's actually a struct, use -> access (isStruct=true)
|
|
165
169
|
// - If typedef says pointer BUT it's a primitive (like u8), don't treat as struct
|
|
166
170
|
// (primitives use forcePointerSemantics for dereference instead)
|
|
171
|
+
// Issue #958: C-header typedef struct types are always treated as struct (pointer semantics)
|
|
167
172
|
const isStruct = callbackTypedefInfo
|
|
168
173
|
? isCallbackPointerParam && typeInfo.isStruct
|
|
169
|
-
: typeInfo.isStruct;
|
|
174
|
+
: typeInfo.isStruct || isTypedefStruct;
|
|
170
175
|
|
|
171
176
|
// Issue #895: Primitive types that become pointers need dereferencing when used as values
|
|
172
177
|
// e.g., "u8 buf" becoming "uint8_t* buf" requires "*buf" when accessing the value
|
|
173
178
|
const isCallbackPointerPrimitive =
|
|
174
179
|
isCallbackPointerParam && !typeInfo.isStruct && !isArray;
|
|
175
180
|
|
|
181
|
+
// Issue #958: typedef struct params need pointer semantics (like callback pointer params)
|
|
182
|
+
const forcePointerSemantics = isCallbackPointerParam || isTypedefStruct;
|
|
183
|
+
|
|
176
184
|
// Register in currentParameters
|
|
177
185
|
const paramInfo = {
|
|
178
186
|
name,
|
|
@@ -183,8 +191,8 @@ class FunctionContextManager {
|
|
|
183
191
|
isCallback: typeInfo.isCallback,
|
|
184
192
|
isString: typeInfo.isString,
|
|
185
193
|
isCallbackPointerPrimitive,
|
|
186
|
-
// Issue #895:
|
|
187
|
-
forcePointerSemantics
|
|
194
|
+
// Issue #895/#958: Force pointer semantics for callback-compatible and typedef struct params
|
|
195
|
+
forcePointerSemantics,
|
|
188
196
|
};
|
|
189
197
|
CodeGenState.currentParameters.set(name, paramInfo);
|
|
190
198
|
|
|
@@ -195,6 +203,7 @@ class FunctionContextManager {
|
|
|
195
203
|
param,
|
|
196
204
|
isArray,
|
|
197
205
|
isConst,
|
|
206
|
+
isTypedefStruct,
|
|
198
207
|
);
|
|
199
208
|
}
|
|
200
209
|
|
|
@@ -320,6 +329,7 @@ class FunctionContextManager {
|
|
|
320
329
|
param: Parser.ParameterContext,
|
|
321
330
|
isArray: boolean,
|
|
322
331
|
isConst: boolean,
|
|
332
|
+
isTypedefStruct = false,
|
|
323
333
|
): void {
|
|
324
334
|
const { typeName, isString } = typeInfo;
|
|
325
335
|
const typeCtx = param.type();
|
|
@@ -358,6 +368,8 @@ class FunctionContextManager {
|
|
|
358
368
|
isString,
|
|
359
369
|
stringCapacity,
|
|
360
370
|
isParameter: true,
|
|
371
|
+
// Issue #958: typedef struct params are already pointers — prevent &arg in call sites
|
|
372
|
+
...(isTypedefStruct && { isPointer: true }),
|
|
361
373
|
};
|
|
362
374
|
CodeGenState.setVariableTypeInfo(name, registeredType);
|
|
363
375
|
}
|
|
@@ -52,6 +52,9 @@ interface IFromASTDeps {
|
|
|
52
52
|
*/
|
|
53
53
|
forcePassByReference?: boolean;
|
|
54
54
|
|
|
55
|
+
/** Issue #958: Check if a type name is a typedef'd struct from C headers */
|
|
56
|
+
isTypedefStructType: (typeName: string) => boolean;
|
|
57
|
+
|
|
55
58
|
/**
|
|
56
59
|
* Issue #895: Force const qualifier from callback typedef signature.
|
|
57
60
|
* When the C typedef has `const T*`, this preserves const on the generated param.
|
|
@@ -137,15 +140,19 @@ class ParameterInputAdapter {
|
|
|
137
140
|
// Determine classification for non-array, non-string types
|
|
138
141
|
const isKnownStruct = deps.isKnownStruct(typeName);
|
|
139
142
|
const isKnownPrimitive = !!deps.typeMap[typeName];
|
|
143
|
+
// Issue #958: C-header typedef struct types need pointer semantics
|
|
144
|
+
const isTypedefStruct = deps.isTypedefStructType(typeName);
|
|
140
145
|
// Issue #895: Don't add auto-const for callback-compatible functions
|
|
141
146
|
// because it would change the signature and break typedef compatibility
|
|
142
147
|
const isAutoConst =
|
|
143
148
|
!deps.isCallbackCompatible && !deps.isModified && !isConst;
|
|
144
149
|
|
|
145
|
-
// Issue #895:
|
|
146
|
-
// when the typedef signature requires a pointer (e.g., opaque types)
|
|
150
|
+
// Issue #895/#958: Force pass-by-reference for callback or typedef struct types
|
|
147
151
|
const isPassByReference =
|
|
148
|
-
deps.forcePassByReference ||
|
|
152
|
+
deps.forcePassByReference ||
|
|
153
|
+
isKnownStruct ||
|
|
154
|
+
isKnownPrimitive ||
|
|
155
|
+
isTypedefStruct;
|
|
149
156
|
|
|
150
157
|
return {
|
|
151
158
|
name,
|
|
@@ -158,9 +165,10 @@ class ParameterInputAdapter {
|
|
|
158
165
|
isString: false,
|
|
159
166
|
isPassByValue: deps.isPassByValue,
|
|
160
167
|
isPassByReference,
|
|
161
|
-
// Issue #895: Force pointer syntax in C++ mode for callback-compatible
|
|
162
|
-
//
|
|
163
|
-
forcePointerSyntax:
|
|
168
|
+
// Issue #895/#958: Force pointer syntax in C++ mode for callback-compatible
|
|
169
|
+
// and typedef struct params (C types expect pointers, not C++ references)
|
|
170
|
+
forcePointerSyntax:
|
|
171
|
+
deps.forcePassByReference || isTypedefStruct || undefined,
|
|
164
172
|
// Issue #895: Preserve const from callback typedef signature
|
|
165
173
|
forceConst: deps.forceConst,
|
|
166
174
|
};
|
|
@@ -7,6 +7,8 @@ interface IFunctionContextCallbacks {
|
|
|
7
7
|
isStructType: (typeName: string) => boolean;
|
|
8
8
|
/** Resolve qualified type identifiers to a type name */
|
|
9
9
|
resolveQualifiedType: (identifiers: string[]) => string;
|
|
10
|
+
/** Issue #958: Check if a type name is a typedef'd struct from C headers */
|
|
11
|
+
isTypedefStructType?: (typeName: string) => boolean;
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
export default IFunctionContextCallbacks;
|
|
@@ -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) {
|