c-next 0.1.61 → 0.1.63
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 +86 -63
- package/grammar/CNext.g4 +3 -17
- package/package.json +1 -1
- package/src/cli/serve/ServeCommand.ts +57 -45
- package/src/lib/__tests__/parseCHeader.mocked.test.ts +145 -0
- package/src/transpiler/Transpiler.ts +603 -613
- package/src/transpiler/__tests__/DualCodePaths.test.ts +5 -1
- package/src/transpiler/__tests__/Transpiler.coverage.test.ts +2 -99
- package/src/transpiler/__tests__/Transpiler.test.ts +3 -26
- package/src/transpiler/data/IncludeTreeWalker.ts +1 -1
- package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +23 -52
- package/src/transpiler/logic/parser/grammar/CNext.interp +1 -3
- package/src/transpiler/logic/parser/grammar/CNextListener.ts +0 -22
- package/src/transpiler/logic/parser/grammar/CNextParser.ts +665 -1084
- package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +0 -14
- package/src/transpiler/logic/symbols/CppSymbolCollector.ts +67 -43
- package/src/transpiler/logic/symbols/cnext/collectors/StructCollector.ts +156 -70
- package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +31 -6
- package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +43 -11
- package/src/transpiler/output/codegen/CodeGenState.ts +811 -0
- package/src/transpiler/output/codegen/CodeGenerator.ts +1410 -2587
- package/src/transpiler/output/codegen/TypeResolver.ts +193 -149
- package/src/transpiler/output/codegen/TypeValidator.ts +148 -370
- package/src/transpiler/output/codegen/__tests__/CodeGenState.test.ts +446 -0
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +2082 -52
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +1 -1
- package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +435 -196
- package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +51 -67
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +495 -471
- package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +227 -66
- package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +55 -58
- package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +288 -275
- package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +101 -144
- package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +195 -133
- package/src/transpiler/output/codegen/assignment/AssignmentContextBuilder.ts +24 -74
- package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +3 -0
- package/src/transpiler/output/codegen/assignment/IAssignmentContext.ts +3 -0
- package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +290 -320
- package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +42 -0
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +76 -2
- package/src/transpiler/output/codegen/generators/GeneratorRegistry.ts +12 -0
- package/src/transpiler/output/codegen/generators/IOrchestrator.ts +5 -1
- package/src/transpiler/output/codegen/generators/__tests__/GeneratorRegistry.test.ts +28 -1
- package/src/transpiler/output/codegen/generators/declarationGenerators/ArrayDimensionUtils.ts +67 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/RegisterGenerator.ts +11 -24
- package/src/transpiler/output/codegen/generators/declarationGenerators/RegisterMacroGenerator.ts +64 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +137 -61
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopedRegisterGenerator.ts +18 -27
- package/src/transpiler/output/codegen/generators/declarationGenerators/StructGenerator.ts +100 -23
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ArrayDimensionUtils.test.ts +125 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +157 -4
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +5 -1
- package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +1 -17
- package/src/transpiler/output/codegen/generators/support/HelperGenerator.ts +23 -22
- package/src/transpiler/output/codegen/helpers/ArrayAccessHelper.ts +129 -0
- package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +54 -61
- package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +40 -44
- package/src/transpiler/output/codegen/helpers/AssignmentTargetExtractor.ts +17 -45
- package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +83 -78
- package/src/transpiler/output/codegen/helpers/CppModeHelper.ts +22 -30
- package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +108 -50
- package/src/transpiler/output/codegen/helpers/FloatBitHelper.ts +16 -31
- package/src/transpiler/output/codegen/helpers/MemberSeparatorResolver.ts +10 -3
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +103 -96
- package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +44 -0
- package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +9 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ArrayAccessHelper.test.ts +479 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +58 -103
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +97 -40
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +223 -128
- package/src/transpiler/output/codegen/helpers/__tests__/CppModeHelper.test.ts +68 -41
- package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +198 -47
- package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +39 -37
- package/src/transpiler/output/codegen/helpers/__tests__/MemberSeparatorResolver.test.ts +1 -0
- package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +191 -453
- package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +201 -0
- package/src/transpiler/output/codegen/helpers/__tests__/TypeGenerationHelper.test.ts +50 -0
- package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +229 -0
- package/src/transpiler/output/codegen/resolution/ScopeResolver.ts +60 -0
- package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +177 -0
- package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +336 -0
- package/src/transpiler/output/codegen/resolution/__tests__/SizeofResolver.test.ts +201 -0
- package/src/transpiler/output/codegen/types/IArrayAccessDeps.ts +23 -0
- package/src/transpiler/output/codegen/types/IArrayAccessInfo.ts +26 -0
- package/src/transpiler/output/codegen/types/IMemberSeparatorDeps.ts +7 -0
- package/src/transpiler/output/codegen/utils/CodegenParserUtils.ts +98 -0
- package/src/transpiler/output/codegen/utils/ExpressionUnwrapper.ts +22 -22
- package/src/transpiler/output/codegen/utils/__tests__/CodegenParserUtils.test.ts +228 -0
- package/src/transpiler/types/IFileResult.ts +0 -4
- package/src/transpiler/types/IPipelineFile.ts +27 -0
- package/src/transpiler/types/IPipelineInput.ts +23 -0
- package/src/transpiler/types/TranspilerState.ts +1 -1
- package/src/utils/FormatUtils.ts +28 -2
- package/src/utils/MapUtils.ts +25 -0
- package/src/utils/PostfixAnalysisUtils.ts +48 -0
- package/src/utils/__tests__/FormatUtils.test.ts +42 -0
- package/src/utils/__tests__/MapUtils.test.ts +85 -0
- package/src/utils/constants/OperatorMappings.ts +19 -0
- package/src/transpiler/logic/StandaloneContextBuilder.ts +0 -150
- package/src/transpiler/logic/__tests__/StandaloneContextBuilder.test.ts +0 -647
- package/src/transpiler/output/codegen/types/ITypeResolverDeps.ts +0 -23
- package/src/transpiler/output/codegen/types/ITypeValidatorDeps.ts +0 -53
- package/src/transpiler/types/ITranspileContext.ts +0 -49
- package/src/transpiler/types/ITranspileContribution.ts +0 -32
|
@@ -323,4 +323,205 @@ describe("SymbolLookupHelper", () => {
|
|
|
323
323
|
);
|
|
324
324
|
});
|
|
325
325
|
});
|
|
326
|
+
|
|
327
|
+
describe("isCNextFunctionCombined", () => {
|
|
328
|
+
it("returns true when in knownFunctions set", () => {
|
|
329
|
+
const knownFunctions = new Set(["myFunc"]);
|
|
330
|
+
expect(
|
|
331
|
+
SymbolLookupHelper.isCNextFunctionCombined(
|
|
332
|
+
knownFunctions,
|
|
333
|
+
null,
|
|
334
|
+
"myFunc",
|
|
335
|
+
),
|
|
336
|
+
).toBe(true);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it("returns true when in symbol table as C-Next function", () => {
|
|
340
|
+
const mockTable = {
|
|
341
|
+
getOverloads: () => [
|
|
342
|
+
{ kind: ESymbolKind.Function, sourceLanguage: ESourceLanguage.CNext },
|
|
343
|
+
],
|
|
344
|
+
};
|
|
345
|
+
expect(
|
|
346
|
+
SymbolLookupHelper.isCNextFunctionCombined(
|
|
347
|
+
new Set(),
|
|
348
|
+
mockTable,
|
|
349
|
+
"myFunc",
|
|
350
|
+
),
|
|
351
|
+
).toBe(true);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it("returns false when not in knownFunctions and not in symbol table", () => {
|
|
355
|
+
const mockTable = {
|
|
356
|
+
getOverloads: () => [],
|
|
357
|
+
};
|
|
358
|
+
expect(
|
|
359
|
+
SymbolLookupHelper.isCNextFunctionCombined(
|
|
360
|
+
new Set(),
|
|
361
|
+
mockTable,
|
|
362
|
+
"myFunc",
|
|
363
|
+
),
|
|
364
|
+
).toBe(false);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it("returns false when knownFunctions is undefined and not in symbol table", () => {
|
|
368
|
+
expect(
|
|
369
|
+
SymbolLookupHelper.isCNextFunctionCombined(undefined, null, "myFunc"),
|
|
370
|
+
).toBe(false);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it("prioritizes knownFunctions over symbol table", () => {
|
|
374
|
+
const knownFunctions = new Set(["myFunc"]);
|
|
375
|
+
const mockTable = {
|
|
376
|
+
getOverloads: () => [
|
|
377
|
+
{ kind: ESymbolKind.Function, sourceLanguage: ESourceLanguage.C },
|
|
378
|
+
],
|
|
379
|
+
};
|
|
380
|
+
expect(
|
|
381
|
+
SymbolLookupHelper.isCNextFunctionCombined(
|
|
382
|
+
knownFunctions,
|
|
383
|
+
mockTable,
|
|
384
|
+
"myFunc",
|
|
385
|
+
),
|
|
386
|
+
).toBe(true);
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
describe("isKnownScope", () => {
|
|
391
|
+
it("returns true when in knownScopes set", () => {
|
|
392
|
+
const knownScopes = new Set(["MyScope"]);
|
|
393
|
+
expect(
|
|
394
|
+
SymbolLookupHelper.isKnownScope(knownScopes, null, "MyScope"),
|
|
395
|
+
).toBe(true);
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
it("returns true when in symbol table as namespace", () => {
|
|
399
|
+
const mockTable = {
|
|
400
|
+
getOverloads: () => [
|
|
401
|
+
{
|
|
402
|
+
kind: ESymbolKind.Namespace,
|
|
403
|
+
sourceLanguage: ESourceLanguage.CNext,
|
|
404
|
+
},
|
|
405
|
+
],
|
|
406
|
+
};
|
|
407
|
+
expect(
|
|
408
|
+
SymbolLookupHelper.isKnownScope(new Set(), mockTable, "MyScope"),
|
|
409
|
+
).toBe(true);
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it("returns false when not in knownScopes and not in symbol table", () => {
|
|
413
|
+
const mockTable = {
|
|
414
|
+
getOverloads: () => [],
|
|
415
|
+
};
|
|
416
|
+
expect(
|
|
417
|
+
SymbolLookupHelper.isKnownScope(new Set(), mockTable, "MyScope"),
|
|
418
|
+
).toBe(false);
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it("returns false when knownScopes is undefined and not in symbol table", () => {
|
|
422
|
+
expect(SymbolLookupHelper.isKnownScope(undefined, null, "MyScope")).toBe(
|
|
423
|
+
false,
|
|
424
|
+
);
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
it("prioritizes knownScopes over symbol table", () => {
|
|
428
|
+
const knownScopes = new Set(["MyScope"]);
|
|
429
|
+
const mockTable = {
|
|
430
|
+
getOverloads: () => [],
|
|
431
|
+
};
|
|
432
|
+
expect(
|
|
433
|
+
SymbolLookupHelper.isKnownScope(knownScopes, mockTable, "MyScope"),
|
|
434
|
+
).toBe(true);
|
|
435
|
+
});
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
describe("isKnownStruct", () => {
|
|
439
|
+
it("returns true when in knownStructs set", () => {
|
|
440
|
+
const knownStructs = new Set(["MyStruct"]);
|
|
441
|
+
expect(
|
|
442
|
+
SymbolLookupHelper.isKnownStruct(
|
|
443
|
+
knownStructs,
|
|
444
|
+
undefined,
|
|
445
|
+
null,
|
|
446
|
+
"MyStruct",
|
|
447
|
+
),
|
|
448
|
+
).toBe(true);
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it("returns true when in knownBitmaps set", () => {
|
|
452
|
+
const knownBitmaps = new Set(["MyBitmap"]);
|
|
453
|
+
expect(
|
|
454
|
+
SymbolLookupHelper.isKnownStruct(
|
|
455
|
+
new Set(),
|
|
456
|
+
knownBitmaps,
|
|
457
|
+
null,
|
|
458
|
+
"MyBitmap",
|
|
459
|
+
),
|
|
460
|
+
).toBe(true);
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
it("returns true when in symbol table via getStructFields", () => {
|
|
464
|
+
const mockTable = {
|
|
465
|
+
getOverloads: () => [],
|
|
466
|
+
getStructFields: (name: string) =>
|
|
467
|
+
name === "CStruct" ? new Map([["field", "int"]]) : undefined,
|
|
468
|
+
};
|
|
469
|
+
expect(
|
|
470
|
+
SymbolLookupHelper.isKnownStruct(
|
|
471
|
+
new Set(),
|
|
472
|
+
new Set(),
|
|
473
|
+
mockTable,
|
|
474
|
+
"CStruct",
|
|
475
|
+
),
|
|
476
|
+
).toBe(true);
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
it("returns false when not found anywhere", () => {
|
|
480
|
+
const mockTable = {
|
|
481
|
+
getOverloads: () => [],
|
|
482
|
+
getStructFields: () => undefined,
|
|
483
|
+
};
|
|
484
|
+
expect(
|
|
485
|
+
SymbolLookupHelper.isKnownStruct(
|
|
486
|
+
new Set(),
|
|
487
|
+
new Set(),
|
|
488
|
+
mockTable,
|
|
489
|
+
"Unknown",
|
|
490
|
+
),
|
|
491
|
+
).toBe(false);
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
it("returns false when all sources are undefined/null", () => {
|
|
495
|
+
expect(
|
|
496
|
+
SymbolLookupHelper.isKnownStruct(undefined, undefined, null, "Unknown"),
|
|
497
|
+
).toBe(false);
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
it("checks knownStructs before knownBitmaps", () => {
|
|
501
|
+
const knownStructs = new Set(["MyType"]);
|
|
502
|
+
const knownBitmaps = new Set<string>();
|
|
503
|
+
expect(
|
|
504
|
+
SymbolLookupHelper.isKnownStruct(
|
|
505
|
+
knownStructs,
|
|
506
|
+
knownBitmaps,
|
|
507
|
+
null,
|
|
508
|
+
"MyType",
|
|
509
|
+
),
|
|
510
|
+
).toBe(true);
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
it("handles symbol table without getStructFields method", () => {
|
|
514
|
+
const mockTable = {
|
|
515
|
+
getOverloads: () => [],
|
|
516
|
+
};
|
|
517
|
+
expect(
|
|
518
|
+
SymbolLookupHelper.isKnownStruct(
|
|
519
|
+
new Set(),
|
|
520
|
+
new Set(),
|
|
521
|
+
mockTable,
|
|
522
|
+
"Unknown",
|
|
523
|
+
),
|
|
524
|
+
).toBe(false);
|
|
525
|
+
});
|
|
526
|
+
});
|
|
326
527
|
});
|
|
@@ -182,6 +182,16 @@ describe("TypeGenerationHelper", () => {
|
|
|
182
182
|
expect(result).toBe("uint32_t");
|
|
183
183
|
});
|
|
184
184
|
|
|
185
|
+
it("returns unknown primitive type unchanged", () => {
|
|
186
|
+
// When primitive type is not in TYPE_MAP, return as-is
|
|
187
|
+
const result = TypeGenerationHelper.generateArrayBaseType(
|
|
188
|
+
"unknownType",
|
|
189
|
+
null,
|
|
190
|
+
false,
|
|
191
|
+
);
|
|
192
|
+
expect(result).toBe("unknownType");
|
|
193
|
+
});
|
|
194
|
+
|
|
185
195
|
it("returns user type unchanged", () => {
|
|
186
196
|
const result = TypeGenerationHelper.generateArrayBaseType(
|
|
187
197
|
null,
|
|
@@ -321,12 +331,52 @@ describe("TypeGenerationHelper", () => {
|
|
|
321
331
|
expect(result).toBe("uint8_t");
|
|
322
332
|
});
|
|
323
333
|
|
|
334
|
+
it("generates array type with primitive via generate()", () => {
|
|
335
|
+
// Array type in type position: u8[10] as the type
|
|
336
|
+
const ctx = getTypeContext("u8[10] arr;");
|
|
337
|
+
expect(ctx).not.toBeNull();
|
|
338
|
+
expect(ctx!.arrayType()).not.toBeNull();
|
|
339
|
+
const result = TypeGenerationHelper.generate(ctx!, defaultDeps);
|
|
340
|
+
expect(result).toBe("uint8_t");
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it("generates array type with user type via generate()", () => {
|
|
344
|
+
// Array of user-defined type: MyStruct[5] as the type
|
|
345
|
+
const ctx = getTypeContext("MyStruct[5] arr;");
|
|
346
|
+
expect(ctx).not.toBeNull();
|
|
347
|
+
expect(ctx!.arrayType()).not.toBeNull();
|
|
348
|
+
const result = TypeGenerationHelper.generate(ctx!, defaultDeps);
|
|
349
|
+
expect(result).toBe("MyStruct");
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it("generates array type with user type needing struct keyword", () => {
|
|
353
|
+
// Array of C struct type that needs 'struct' prefix
|
|
354
|
+
const ctx = getTypeContext("CStruct[3] arr;");
|
|
355
|
+
expect(ctx).not.toBeNull();
|
|
356
|
+
expect(ctx!.arrayType()).not.toBeNull();
|
|
357
|
+
const result = TypeGenerationHelper.generate(ctx!, {
|
|
358
|
+
...defaultDeps,
|
|
359
|
+
checkNeedsStructKeyword: (name) => name === "CStruct",
|
|
360
|
+
});
|
|
361
|
+
expect(result).toBe("struct CStruct");
|
|
362
|
+
});
|
|
363
|
+
|
|
324
364
|
it("generates void return type", () => {
|
|
325
365
|
const ctx = getFunctionReturnType("void test() { }");
|
|
326
366
|
expect(ctx).not.toBeNull();
|
|
327
367
|
const result = TypeGenerationHelper.generate(ctx!, defaultDeps);
|
|
328
368
|
expect(result).toBe("void");
|
|
329
369
|
});
|
|
370
|
+
|
|
371
|
+
it("passes through C++ template types unchanged (fallback)", () => {
|
|
372
|
+
// C++ template types like FlexCAN_T4<CAN1> hit the fallback path
|
|
373
|
+
// and are passed through unchanged for C++ output
|
|
374
|
+
const ctx = getTypeContext("FlexCAN_T4<CAN1> bus;");
|
|
375
|
+
expect(ctx).not.toBeNull();
|
|
376
|
+
expect(ctx!.templateType()).not.toBeNull();
|
|
377
|
+
const result = TypeGenerationHelper.generate(ctx!, defaultDeps);
|
|
378
|
+
expect(result).toBe("FlexCAN_T4<CAN1>");
|
|
379
|
+
});
|
|
330
380
|
});
|
|
331
381
|
|
|
332
382
|
describe("getRequiredInclude", () => {
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EnumTypeResolver - Handles enum type inference from expressions
|
|
3
|
+
*
|
|
4
|
+
* Extracted from CodeGenerator to reduce complexity.
|
|
5
|
+
* Uses CodeGenState for all state access.
|
|
6
|
+
*
|
|
7
|
+
* ADR-017: Extract enum type from expressions for type-safe comparisons.
|
|
8
|
+
* Handles patterns:
|
|
9
|
+
* - Variable of enum type: `currentState` -> 'State'
|
|
10
|
+
* - Enum member access: `State.IDLE` -> 'State'
|
|
11
|
+
* - Scoped enum member: `Motor.State.IDLE` -> 'Motor_State'
|
|
12
|
+
* - ADR-016: this.State.IDLE -> 'CurrentScope_State'
|
|
13
|
+
* - ADR-016: this.variable -> enum type if variable is of enum type
|
|
14
|
+
* - Function calls returning enum types
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import * as Parser from "../../../logic/parser/grammar/CNextParser";
|
|
18
|
+
import CodeGenState from "../CodeGenState";
|
|
19
|
+
import TypeResolver from "../TypeResolver";
|
|
20
|
+
import ExpressionUnwrapper from "../utils/ExpressionUnwrapper";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Resolves enum types from expressions.
|
|
24
|
+
* All methods are static - uses CodeGenState for state access.
|
|
25
|
+
*/
|
|
26
|
+
export default class EnumTypeResolver {
|
|
27
|
+
/**
|
|
28
|
+
* Extract enum type from an expression.
|
|
29
|
+
* Returns the enum type name if the expression is an enum value, null otherwise.
|
|
30
|
+
*/
|
|
31
|
+
static resolve(
|
|
32
|
+
ctx: Parser.ExpressionContext | Parser.RelationalExpressionContext,
|
|
33
|
+
): string | null {
|
|
34
|
+
const text = ctx.getText();
|
|
35
|
+
|
|
36
|
+
// Check if it's a function call returning an enum
|
|
37
|
+
const enumReturnType = this.getFunctionCallEnumType(text);
|
|
38
|
+
if (enumReturnType) {
|
|
39
|
+
return enumReturnType;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check if it's a simple identifier that's an enum variable
|
|
43
|
+
if (/^[a-zA-Z_]\w*$/.exec(text)) {
|
|
44
|
+
const typeInfo = CodeGenState.typeRegistry.get(text);
|
|
45
|
+
if (typeInfo?.isEnum && typeInfo.enumTypeName) {
|
|
46
|
+
return typeInfo.enumTypeName;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Check member access patterns: EnumType.MEMBER, Scope.EnumType.MEMBER, etc.
|
|
51
|
+
const memberResult = this.getEnumTypeFromMemberAccess(text.split("."));
|
|
52
|
+
if (memberResult) {
|
|
53
|
+
return memberResult;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Fallback: use TypeResolver to resolve the full expression type through
|
|
57
|
+
// struct member chains (e.g. global.config.inputs[0].assignedValue -> EValueId)
|
|
58
|
+
return this.resolveViaTypeResolver(ctx);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Fallback resolution via TypeResolver for complex expressions.
|
|
63
|
+
* Handles struct member chains like global.struct.field that resolve to enum types.
|
|
64
|
+
*/
|
|
65
|
+
private static resolveViaTypeResolver(
|
|
66
|
+
ctx: Parser.ExpressionContext | Parser.RelationalExpressionContext,
|
|
67
|
+
): string | null {
|
|
68
|
+
// ExpressionContext has getPostfixExpression, RelationalExpressionContext does not
|
|
69
|
+
if (!("ternaryExpression" in ctx)) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
const postfix = ExpressionUnwrapper.getPostfixExpression(ctx);
|
|
73
|
+
if (!postfix) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const resolvedType = TypeResolver.getPostfixExpressionType(postfix);
|
|
77
|
+
if (resolvedType && CodeGenState.isKnownEnum(resolvedType)) {
|
|
78
|
+
return resolvedType;
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Check if parts represent an enum member access and return the enum type.
|
|
85
|
+
*/
|
|
86
|
+
private static getEnumTypeFromMemberAccess(parts: string[]): string | null {
|
|
87
|
+
if (parts.length < 2) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ADR-016: Check this.State.IDLE pattern
|
|
92
|
+
const thisEnumType = this.getEnumTypeFromThisEnum(parts);
|
|
93
|
+
if (thisEnumType) return thisEnumType;
|
|
94
|
+
|
|
95
|
+
// Issue #478: Check global.Enum.Member pattern
|
|
96
|
+
const globalEnumType = this.getEnumTypeFromGlobalEnum(parts);
|
|
97
|
+
if (globalEnumType) return globalEnumType;
|
|
98
|
+
|
|
99
|
+
// ADR-016: Check this.variable pattern
|
|
100
|
+
const thisVarType = this.getEnumTypeFromThisVariable(parts);
|
|
101
|
+
if (thisVarType) return thisVarType;
|
|
102
|
+
|
|
103
|
+
// Check simple enum: State.IDLE
|
|
104
|
+
const possibleEnum = parts[0];
|
|
105
|
+
if (CodeGenState.isKnownEnum(possibleEnum)) {
|
|
106
|
+
return possibleEnum;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Check scoped enum: Motor.State.IDLE -> Motor_State
|
|
110
|
+
return this.getEnumTypeFromScopedEnum(parts);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* ADR-016: Check this.State.IDLE pattern (this.Enum.Member inside scope)
|
|
115
|
+
*/
|
|
116
|
+
private static getEnumTypeFromThisEnum(parts: string[]): string | null {
|
|
117
|
+
if (parts[0] !== "this" || !CodeGenState.currentScope || parts.length < 3) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
const enumName = parts[1];
|
|
121
|
+
const scopedEnumName = `${CodeGenState.currentScope}_${enumName}`;
|
|
122
|
+
return CodeGenState.isKnownEnum(scopedEnumName) ? scopedEnumName : null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Issue #478: Check global.Enum.Member pattern (global.ECategory.CAT_A)
|
|
127
|
+
*/
|
|
128
|
+
private static getEnumTypeFromGlobalEnum(parts: string[]): string | null {
|
|
129
|
+
if (parts[0] !== "global" || parts.length < 3) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
const enumName = parts[1];
|
|
133
|
+
return CodeGenState.isKnownEnum(enumName) ? enumName : null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* ADR-016: Check this.variable pattern (this.varName where varName is enum type)
|
|
138
|
+
*/
|
|
139
|
+
private static getEnumTypeFromThisVariable(parts: string[]): string | null {
|
|
140
|
+
if (
|
|
141
|
+
parts[0] !== "this" ||
|
|
142
|
+
!CodeGenState.currentScope ||
|
|
143
|
+
parts.length !== 2
|
|
144
|
+
) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
const varName = parts[1];
|
|
148
|
+
const scopedVarName = `${CodeGenState.currentScope}_${varName}`;
|
|
149
|
+
const typeInfo = CodeGenState.typeRegistry.get(scopedVarName);
|
|
150
|
+
if (typeInfo?.isEnum && typeInfo.enumTypeName) {
|
|
151
|
+
return typeInfo.enumTypeName;
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Check scoped enum: Motor.State.IDLE -> Motor_State
|
|
158
|
+
*/
|
|
159
|
+
private static getEnumTypeFromScopedEnum(parts: string[]): string | null {
|
|
160
|
+
if (parts.length < 3) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
const scopeName = parts[0];
|
|
164
|
+
const enumName = parts[1];
|
|
165
|
+
const scopedEnumName = `${scopeName}_${enumName}`;
|
|
166
|
+
return CodeGenState.isKnownEnum(scopedEnumName) ? scopedEnumName : null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Check if an expression is a function call returning an enum type.
|
|
171
|
+
* Handles patterns:
|
|
172
|
+
* - func() or func(args) - global function
|
|
173
|
+
* - Scope.method() or Scope.method(args) - scope method from outside
|
|
174
|
+
* - this.method() or this.method(args) - scope method from inside
|
|
175
|
+
* - global.func() or global.func(args) - global function from inside scope
|
|
176
|
+
* - global.Scope.method() or global.Scope.method(args) - scope method from inside another scope
|
|
177
|
+
*/
|
|
178
|
+
private static getFunctionCallEnumType(text: string): string | null {
|
|
179
|
+
// Check if this looks like a function call (contains parentheses)
|
|
180
|
+
const parenIndex = text.indexOf("(");
|
|
181
|
+
if (parenIndex === -1) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Extract the function reference (everything before the opening paren)
|
|
186
|
+
const funcRef = text.substring(0, parenIndex);
|
|
187
|
+
const parts = funcRef.split(".");
|
|
188
|
+
|
|
189
|
+
let fullFuncName: string | null = null;
|
|
190
|
+
|
|
191
|
+
if (parts.length === 1) {
|
|
192
|
+
// Simple function call: func()
|
|
193
|
+
fullFuncName = parts[0];
|
|
194
|
+
} else if (parts.length === 2) {
|
|
195
|
+
if (parts[0] === "this" && CodeGenState.currentScope) {
|
|
196
|
+
// this.method() -> Scope_method
|
|
197
|
+
fullFuncName = `${CodeGenState.currentScope}_${parts[1]}`;
|
|
198
|
+
} else if (parts[0] === "global") {
|
|
199
|
+
// global.func() -> func
|
|
200
|
+
fullFuncName = parts[1];
|
|
201
|
+
} else if (CodeGenState.isKnownScope(parts[0])) {
|
|
202
|
+
// Scope.method() -> Scope_method
|
|
203
|
+
fullFuncName = `${parts[0]}_${parts[1]}`;
|
|
204
|
+
}
|
|
205
|
+
} else if (parts.length === 3) {
|
|
206
|
+
if (parts[0] === "global" && CodeGenState.isKnownScope(parts[1])) {
|
|
207
|
+
// global.Scope.method() -> Scope_method
|
|
208
|
+
fullFuncName = `${parts[1]}_${parts[2]}`;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (!fullFuncName) {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Look up the function's return type
|
|
217
|
+
const returnType = CodeGenState.getFunctionReturnType(fullFuncName);
|
|
218
|
+
if (!returnType) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Check if the return type is an enum
|
|
223
|
+
if (CodeGenState.isKnownEnum(returnType)) {
|
|
224
|
+
return returnType;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ScopeResolver - Handles scope visibility and access validation
|
|
3
|
+
*
|
|
4
|
+
* Extracted from CodeGenerator to reduce complexity.
|
|
5
|
+
* Uses CodeGenState for all state access.
|
|
6
|
+
*
|
|
7
|
+
* ADR-016: Validates cross-scope member access visibility rules.
|
|
8
|
+
* Issue #165: Enforces that:
|
|
9
|
+
* - Cannot reference own scope by name (must use this. prefix)
|
|
10
|
+
* - Cannot access private members from outside the scope
|
|
11
|
+
* - Exception: global.Scope.member is allowed for explicit qualification
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import CodeGenState from "../CodeGenState";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Resolves scope visibility and validates cross-scope access.
|
|
18
|
+
* All methods are static - uses CodeGenState for state access.
|
|
19
|
+
*/
|
|
20
|
+
export default class ScopeResolver {
|
|
21
|
+
/**
|
|
22
|
+
* Validate cross-scope visibility for member access.
|
|
23
|
+
* Throws an error if the access violates visibility rules.
|
|
24
|
+
*
|
|
25
|
+
* @param scopeName - The scope being accessed
|
|
26
|
+
* @param memberName - The member being accessed
|
|
27
|
+
* @param isGlobalAccess - Whether this is a global.Scope.member access
|
|
28
|
+
*/
|
|
29
|
+
static validateCrossScopeVisibility(
|
|
30
|
+
scopeName: string,
|
|
31
|
+
memberName: string,
|
|
32
|
+
isGlobalAccess: boolean = false,
|
|
33
|
+
): void {
|
|
34
|
+
// Error if referencing own scope by name (must use this. prefix)
|
|
35
|
+
// Exception: global.Scope.member is allowed for explicit qualification
|
|
36
|
+
if (!isGlobalAccess && CodeGenState.currentScope === scopeName) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
`Error: Cannot reference own scope '${scopeName}' by name. ` +
|
|
39
|
+
`Use 'this.${memberName}' instead of '${scopeName}.${memberName}'`,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Check private member access (skip for own scope - we can access our own privates)
|
|
44
|
+
const isOwnScope = CodeGenState.currentScope === scopeName;
|
|
45
|
+
if (!isOwnScope) {
|
|
46
|
+
const visibility = CodeGenState.symbols?.scopeMemberVisibility
|
|
47
|
+
.get(scopeName)
|
|
48
|
+
?.get(memberName);
|
|
49
|
+
if (visibility === "private") {
|
|
50
|
+
const context = CodeGenState.currentScope
|
|
51
|
+
? `from scope '${CodeGenState.currentScope}'`
|
|
52
|
+
: "from outside the scope";
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Cannot access private member '${memberName}' of scope '${scopeName}' ${context}. ` +
|
|
55
|
+
`Only public members are accessible outside their scope.`,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|