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.
Files changed (104) hide show
  1. package/README.md +86 -63
  2. package/grammar/CNext.g4 +3 -17
  3. package/package.json +1 -1
  4. package/src/cli/serve/ServeCommand.ts +57 -45
  5. package/src/lib/__tests__/parseCHeader.mocked.test.ts +145 -0
  6. package/src/transpiler/Transpiler.ts +603 -613
  7. package/src/transpiler/__tests__/DualCodePaths.test.ts +5 -1
  8. package/src/transpiler/__tests__/Transpiler.coverage.test.ts +2 -99
  9. package/src/transpiler/__tests__/Transpiler.test.ts +3 -26
  10. package/src/transpiler/data/IncludeTreeWalker.ts +1 -1
  11. package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +23 -52
  12. package/src/transpiler/logic/parser/grammar/CNext.interp +1 -3
  13. package/src/transpiler/logic/parser/grammar/CNextListener.ts +0 -22
  14. package/src/transpiler/logic/parser/grammar/CNextParser.ts +665 -1084
  15. package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +0 -14
  16. package/src/transpiler/logic/symbols/CppSymbolCollector.ts +67 -43
  17. package/src/transpiler/logic/symbols/cnext/collectors/StructCollector.ts +156 -70
  18. package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +31 -6
  19. package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +43 -11
  20. package/src/transpiler/output/codegen/CodeGenState.ts +811 -0
  21. package/src/transpiler/output/codegen/CodeGenerator.ts +1410 -2587
  22. package/src/transpiler/output/codegen/TypeResolver.ts +193 -149
  23. package/src/transpiler/output/codegen/TypeValidator.ts +148 -370
  24. package/src/transpiler/output/codegen/__tests__/CodeGenState.test.ts +446 -0
  25. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +2082 -52
  26. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +1 -1
  27. package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +435 -196
  28. package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +51 -67
  29. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +495 -471
  30. package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +227 -66
  31. package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +55 -58
  32. package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +288 -275
  33. package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +101 -144
  34. package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +195 -133
  35. package/src/transpiler/output/codegen/assignment/AssignmentContextBuilder.ts +24 -74
  36. package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +3 -0
  37. package/src/transpiler/output/codegen/assignment/IAssignmentContext.ts +3 -0
  38. package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +290 -320
  39. package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +42 -0
  40. package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +76 -2
  41. package/src/transpiler/output/codegen/generators/GeneratorRegistry.ts +12 -0
  42. package/src/transpiler/output/codegen/generators/IOrchestrator.ts +5 -1
  43. package/src/transpiler/output/codegen/generators/__tests__/GeneratorRegistry.test.ts +28 -1
  44. package/src/transpiler/output/codegen/generators/declarationGenerators/ArrayDimensionUtils.ts +67 -0
  45. package/src/transpiler/output/codegen/generators/declarationGenerators/RegisterGenerator.ts +11 -24
  46. package/src/transpiler/output/codegen/generators/declarationGenerators/RegisterMacroGenerator.ts +64 -0
  47. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +137 -61
  48. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopedRegisterGenerator.ts +18 -27
  49. package/src/transpiler/output/codegen/generators/declarationGenerators/StructGenerator.ts +100 -23
  50. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ArrayDimensionUtils.test.ts +125 -0
  51. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +157 -4
  52. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +5 -1
  53. package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +1 -17
  54. package/src/transpiler/output/codegen/generators/support/HelperGenerator.ts +23 -22
  55. package/src/transpiler/output/codegen/helpers/ArrayAccessHelper.ts +129 -0
  56. package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +54 -61
  57. package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +40 -44
  58. package/src/transpiler/output/codegen/helpers/AssignmentTargetExtractor.ts +17 -45
  59. package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +83 -78
  60. package/src/transpiler/output/codegen/helpers/CppModeHelper.ts +22 -30
  61. package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +108 -50
  62. package/src/transpiler/output/codegen/helpers/FloatBitHelper.ts +16 -31
  63. package/src/transpiler/output/codegen/helpers/MemberSeparatorResolver.ts +10 -3
  64. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +103 -96
  65. package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +44 -0
  66. package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +9 -0
  67. package/src/transpiler/output/codegen/helpers/__tests__/ArrayAccessHelper.test.ts +479 -0
  68. package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +58 -103
  69. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +97 -40
  70. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +223 -128
  71. package/src/transpiler/output/codegen/helpers/__tests__/CppModeHelper.test.ts +68 -41
  72. package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +198 -47
  73. package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +39 -37
  74. package/src/transpiler/output/codegen/helpers/__tests__/MemberSeparatorResolver.test.ts +1 -0
  75. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +191 -453
  76. package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +201 -0
  77. package/src/transpiler/output/codegen/helpers/__tests__/TypeGenerationHelper.test.ts +50 -0
  78. package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +229 -0
  79. package/src/transpiler/output/codegen/resolution/ScopeResolver.ts +60 -0
  80. package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +177 -0
  81. package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +336 -0
  82. package/src/transpiler/output/codegen/resolution/__tests__/SizeofResolver.test.ts +201 -0
  83. package/src/transpiler/output/codegen/types/IArrayAccessDeps.ts +23 -0
  84. package/src/transpiler/output/codegen/types/IArrayAccessInfo.ts +26 -0
  85. package/src/transpiler/output/codegen/types/IMemberSeparatorDeps.ts +7 -0
  86. package/src/transpiler/output/codegen/utils/CodegenParserUtils.ts +98 -0
  87. package/src/transpiler/output/codegen/utils/ExpressionUnwrapper.ts +22 -22
  88. package/src/transpiler/output/codegen/utils/__tests__/CodegenParserUtils.test.ts +228 -0
  89. package/src/transpiler/types/IFileResult.ts +0 -4
  90. package/src/transpiler/types/IPipelineFile.ts +27 -0
  91. package/src/transpiler/types/IPipelineInput.ts +23 -0
  92. package/src/transpiler/types/TranspilerState.ts +1 -1
  93. package/src/utils/FormatUtils.ts +28 -2
  94. package/src/utils/MapUtils.ts +25 -0
  95. package/src/utils/PostfixAnalysisUtils.ts +48 -0
  96. package/src/utils/__tests__/FormatUtils.test.ts +42 -0
  97. package/src/utils/__tests__/MapUtils.test.ts +85 -0
  98. package/src/utils/constants/OperatorMappings.ts +19 -0
  99. package/src/transpiler/logic/StandaloneContextBuilder.ts +0 -150
  100. package/src/transpiler/logic/__tests__/StandaloneContextBuilder.test.ts +0 -647
  101. package/src/transpiler/output/codegen/types/ITypeResolverDeps.ts +0 -23
  102. package/src/transpiler/output/codegen/types/ITypeValidatorDeps.ts +0 -53
  103. package/src/transpiler/types/ITranspileContext.ts +0 -49
  104. 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
+ }