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
@@ -0,0 +1,177 @@
1
+ /**
2
+ * SizeofResolver - Handles sizeof expression generation
3
+ *
4
+ * Extracted from CodeGenerator to reduce complexity.
5
+ * Uses CodeGenState for all state access.
6
+ *
7
+ * ADR-023: sizeof expression handling with safety checks:
8
+ * - E0601: sizeof on array parameter is error (returns pointer size)
9
+ * - E0602: Side effects in sizeof are error (MISRA C:2012 Rule 13.6)
10
+ */
11
+
12
+ import * as Parser from "../../../logic/parser/grammar/CNextParser";
13
+ import CodeGenState from "../CodeGenState";
14
+ import ExpressionUnwrapper from "../utils/ExpressionUnwrapper";
15
+
16
+ /**
17
+ * Callbacks for operations that require CodeGenerator context.
18
+ * These are the minimal dependencies that can't be replaced with CodeGenState.
19
+ */
20
+ interface ISizeofCallbacks {
21
+ generateType: (ctx: Parser.TypeContext) => string;
22
+ generateExpression: (ctx: Parser.ExpressionContext) => string;
23
+ hasSideEffects: (ctx: Parser.ExpressionContext) => boolean;
24
+ }
25
+
26
+ /**
27
+ * Resolves sizeof expressions to C code.
28
+ */
29
+ export default class SizeofResolver {
30
+ /**
31
+ * Generate sizeof expression.
32
+ * sizeof(type) -> sizeof(c_type)
33
+ * sizeof(variable) -> sizeof(variable)
34
+ */
35
+ static generate(
36
+ ctx: Parser.SizeofExpressionContext,
37
+ callbacks: ISizeofCallbacks,
38
+ ): string {
39
+ // Check if it's sizeof(type) or sizeof(expression)
40
+ // Note: Due to grammar ambiguity, sizeof(variable) may parse as sizeof(type)
41
+ // when the variable name matches userType (just an identifier)
42
+ if (ctx.type()) {
43
+ return this.sizeofType(ctx.type()!, callbacks);
44
+ }
45
+ return this.sizeofExpression(ctx.expression()!, callbacks);
46
+ }
47
+
48
+ /**
49
+ * Handle sizeof(type) - may actually be sizeof(variable) due to grammar ambiguity
50
+ */
51
+ private static sizeofType(
52
+ typeCtx: Parser.TypeContext,
53
+ callbacks: ISizeofCallbacks,
54
+ ): string {
55
+ // qualifiedType matches IDENTIFIER.IDENTIFIER, could be struct.member
56
+ if (typeCtx.qualifiedType()) {
57
+ const result = this.sizeofQualifiedType(typeCtx.qualifiedType()!);
58
+ if (result) return result;
59
+ // Fall through to generateType for actual type references (Scope.Type)
60
+ }
61
+
62
+ // userType is just IDENTIFIER, could be a variable reference
63
+ if (typeCtx.userType()) {
64
+ return this.sizeofUserType(typeCtx.getText());
65
+ }
66
+
67
+ // It's a primitive or other type - generate normally
68
+ return `sizeof(${callbacks.generateType(typeCtx)})`;
69
+ }
70
+
71
+ /**
72
+ * Handle sizeof(qualified.type) - may be struct.member access
73
+ * Returns null if this is actually a type reference (Scope.Type)
74
+ */
75
+ private static sizeofQualifiedType(
76
+ qualifiedCtx: Parser.QualifiedTypeContext,
77
+ ): string | null {
78
+ const identifiers = qualifiedCtx.IDENTIFIER();
79
+ const firstName = identifiers[0].getText();
80
+ const memberName = identifiers[1].getText();
81
+
82
+ // Check if first identifier is a local variable (struct instance)
83
+ if (CodeGenState.localVariables.has(firstName)) {
84
+ return `sizeof(${firstName}.${memberName})`;
85
+ }
86
+
87
+ // Check if first identifier is a parameter (struct parameter)
88
+ const paramInfo = CodeGenState.currentParameters.get(firstName);
89
+ if (paramInfo) {
90
+ const sep = paramInfo.isStruct ? "->" : ".";
91
+ return `sizeof(${firstName}${sep}${memberName})`;
92
+ }
93
+
94
+ // Check if first identifier is a global variable
95
+ // If not a scope or enum, it's likely a global struct variable
96
+ if (
97
+ !CodeGenState.isKnownScope(firstName) &&
98
+ !CodeGenState.isKnownEnum(firstName)
99
+ ) {
100
+ return `sizeof(${firstName}.${memberName})`;
101
+ }
102
+
103
+ // It's an actual type reference (Scope.Type), return null to fall through
104
+ return null;
105
+ }
106
+
107
+ /**
108
+ * Handle sizeof(identifier) - could be variable or type name
109
+ */
110
+ private static sizeofUserType(varName: string): string {
111
+ // Check if it's a known parameter
112
+ const paramInfo = CodeGenState.currentParameters.get(varName);
113
+ if (paramInfo) {
114
+ return this.sizeofParameter(varName, paramInfo);
115
+ }
116
+
117
+ // Check if it's a known local variable, struct type, or enum type
118
+ // For all these cases, generate sizeof(name) directly
119
+ // Unknown identifiers are also treated as variables for safety
120
+ return `sizeof(${varName})`;
121
+ }
122
+
123
+ /**
124
+ * Handle sizeof on a parameter - validates and generates appropriate code
125
+ */
126
+ private static sizeofParameter(
127
+ varName: string,
128
+ paramInfo: { isArray?: boolean; isCallback?: boolean; isStruct?: boolean },
129
+ ): string {
130
+ // E0601: Array parameters decay to pointers
131
+ if (paramInfo.isArray) {
132
+ this.throwArrayParamSizeofError(varName);
133
+ }
134
+ // For pass-by-reference parameters (non-array, non-callback, non-struct),
135
+ // use pointer dereference
136
+ if (!paramInfo.isCallback && !paramInfo.isStruct) {
137
+ return `sizeof(*${varName})`;
138
+ }
139
+ return `sizeof(${varName})`;
140
+ }
141
+
142
+ /**
143
+ * Throw E0601 error for sizeof on array parameter
144
+ */
145
+ private static throwArrayParamSizeofError(varName: string): never {
146
+ throw new Error(
147
+ `Error[E0601]: sizeof() on array parameter '${varName}' returns pointer size. ` +
148
+ `Use ${varName}.length for element count or sizeof(elementType) * ${varName}.length for bytes`,
149
+ );
150
+ }
151
+
152
+ /**
153
+ * Handle sizeof(expression) with validation
154
+ */
155
+ private static sizeofExpression(
156
+ expr: Parser.ExpressionContext,
157
+ callbacks: ISizeofCallbacks,
158
+ ): string {
159
+ // E0601: Check if expression is an array parameter
160
+ const varName = ExpressionUnwrapper.getSimpleIdentifier(expr);
161
+ if (varName) {
162
+ const paramInfo = CodeGenState.currentParameters.get(varName);
163
+ if (paramInfo?.isArray) {
164
+ this.throwArrayParamSizeofError(varName);
165
+ }
166
+ }
167
+
168
+ // E0602: Check for side effects
169
+ if (callbacks.hasSideEffects(expr)) {
170
+ throw new Error(
171
+ `Error[E0602]: sizeof() operand must not have side effects (MISRA C:2012 Rule 13.6)`,
172
+ );
173
+ }
174
+
175
+ return `sizeof(${callbacks.generateExpression(expr)})`;
176
+ }
177
+ }
@@ -0,0 +1,336 @@
1
+ /**
2
+ * Tests for EnumTypeResolver - enum type inference from expressions
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach } from "vitest";
6
+ import EnumTypeResolver from "../EnumTypeResolver";
7
+ import CodeGenState from "../../CodeGenState";
8
+ import SymbolTable from "../../../../logic/symbols/SymbolTable";
9
+ import ICodeGenSymbols from "../../../../types/ICodeGenSymbols";
10
+
11
+ describe("EnumTypeResolver", () => {
12
+ const createMockSymbols = (
13
+ overrides: Partial<ICodeGenSymbols> = {},
14
+ ): ICodeGenSymbols =>
15
+ ({
16
+ knownScopes: new Set(),
17
+ knownEnums: new Set(),
18
+ knownBitmaps: new Set(),
19
+ knownStructs: new Set(),
20
+ knownRegisters: new Set(),
21
+ bitmapBitWidth: new Map(),
22
+ bitmapFields: new Map(),
23
+ bitmapBackingType: new Map(),
24
+ enumMembers: new Map(),
25
+ structFields: new Map(),
26
+ structFieldArrays: new Map(),
27
+ structFieldDimensions: new Map(),
28
+ functionReturnTypes: new Map(),
29
+ scopeMembers: new Map(),
30
+ scopeMemberVisibility: new Map(),
31
+ scopedRegisters: new Map(),
32
+ registerMemberAccess: new Map(),
33
+ registerMemberTypes: new Map(),
34
+ registerBaseAddresses: new Map(),
35
+ registerMemberOffsets: new Map(),
36
+ registerMemberCTypes: new Map(),
37
+ scopeVariableUsage: new Map(),
38
+ scopePrivateConstValues: new Map(),
39
+ getSingleFunctionForVariable: () => null,
40
+ hasPublicSymbols: () => false,
41
+ ...overrides,
42
+ }) as ICodeGenSymbols;
43
+
44
+ beforeEach(() => {
45
+ CodeGenState.reset();
46
+ });
47
+
48
+ describe("resolve() - function call patterns", () => {
49
+ it("resolves function call returning enum type", () => {
50
+ CodeGenState.symbols = createMockSymbols({
51
+ knownEnums: new Set(["State"]),
52
+ functionReturnTypes: new Map([["getState", "State"]]),
53
+ });
54
+
55
+ const mockCtx = { getText: () => "getState()" };
56
+ expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("State");
57
+ });
58
+
59
+ it("resolves this.method() returning enum type", () => {
60
+ CodeGenState.currentScope = "Motor";
61
+ CodeGenState.symbols = createMockSymbols({
62
+ knownEnums: new Set(["State"]),
63
+ functionReturnTypes: new Map([["Motor_getState", "State"]]),
64
+ });
65
+
66
+ const mockCtx = { getText: () => "this.getState()" };
67
+ expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("State");
68
+ });
69
+
70
+ it("resolves global.func() returning enum type", () => {
71
+ CodeGenState.symbols = createMockSymbols({
72
+ knownEnums: new Set(["State"]),
73
+ functionReturnTypes: new Map([["getGlobalState", "State"]]),
74
+ });
75
+
76
+ const mockCtx = { getText: () => "global.getGlobalState()" };
77
+ expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("State");
78
+ });
79
+
80
+ it("resolves Scope.method() returning enum type", () => {
81
+ CodeGenState.symbols = createMockSymbols({
82
+ knownScopes: new Set(["Motor"]),
83
+ knownEnums: new Set(["State"]),
84
+ functionReturnTypes: new Map([["Motor_getState", "State"]]),
85
+ });
86
+
87
+ const mockCtx = { getText: () => "Motor.getState()" };
88
+ expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("State");
89
+ });
90
+
91
+ it("resolves global.Scope.method() returning enum type", () => {
92
+ CodeGenState.symbols = createMockSymbols({
93
+ knownScopes: new Set(["Motor"]),
94
+ knownEnums: new Set(["State"]),
95
+ functionReturnTypes: new Map([["Motor_getState", "State"]]),
96
+ });
97
+
98
+ const mockCtx = { getText: () => "global.Motor.getState()" };
99
+ expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("State");
100
+ });
101
+
102
+ it("returns null for function returning non-enum type", () => {
103
+ CodeGenState.symbols = createMockSymbols({
104
+ functionReturnTypes: new Map([["getValue", "u32"]]),
105
+ });
106
+
107
+ const mockCtx = { getText: () => "getValue()" };
108
+ expect(EnumTypeResolver.resolve(mockCtx as never)).toBeNull();
109
+ });
110
+
111
+ it("returns null for unknown function", () => {
112
+ CodeGenState.symbols = createMockSymbols();
113
+
114
+ const mockCtx = { getText: () => "unknownFunc()" };
115
+ expect(EnumTypeResolver.resolve(mockCtx as never)).toBeNull();
116
+ });
117
+ });
118
+
119
+ describe("resolve() - simple identifier patterns", () => {
120
+ it("resolves enum variable by type registry lookup", () => {
121
+ CodeGenState.symbols = createMockSymbols({
122
+ knownEnums: new Set(["State"]),
123
+ });
124
+ CodeGenState.typeRegistry.set("currentState", {
125
+ baseType: "State",
126
+ bitWidth: 0,
127
+ isArray: false,
128
+ isConst: false,
129
+ isEnum: true,
130
+ enumTypeName: "State",
131
+ });
132
+
133
+ const mockCtx = { getText: () => "currentState" };
134
+ expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("State");
135
+ });
136
+
137
+ it("returns null for non-enum variable", () => {
138
+ CodeGenState.typeRegistry.set("count", {
139
+ baseType: "u32",
140
+ bitWidth: 32,
141
+ isArray: false,
142
+ isConst: false,
143
+ });
144
+
145
+ const mockCtx = { getText: () => "count" };
146
+ expect(EnumTypeResolver.resolve(mockCtx as never)).toBeNull();
147
+ });
148
+ });
149
+
150
+ describe("resolve() - member access patterns", () => {
151
+ it("resolves simple enum member access: State.IDLE", () => {
152
+ CodeGenState.symbols = createMockSymbols({
153
+ knownEnums: new Set(["State"]),
154
+ });
155
+
156
+ const mockCtx = { getText: () => "State.IDLE" };
157
+ expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("State");
158
+ });
159
+
160
+ it("resolves scoped enum: Motor.State.IDLE -> Motor_State", () => {
161
+ CodeGenState.symbols = createMockSymbols({
162
+ knownEnums: new Set(["Motor_State"]),
163
+ });
164
+
165
+ const mockCtx = { getText: () => "Motor.State.IDLE" };
166
+ expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("Motor_State");
167
+ });
168
+
169
+ it("resolves this.Enum.MEMBER inside scope", () => {
170
+ CodeGenState.currentScope = "Motor";
171
+ CodeGenState.symbols = createMockSymbols({
172
+ knownEnums: new Set(["Motor_State"]),
173
+ });
174
+
175
+ const mockCtx = { getText: () => "this.State.IDLE" };
176
+ expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("Motor_State");
177
+ });
178
+
179
+ it("resolves global.Enum.MEMBER pattern", () => {
180
+ CodeGenState.symbols = createMockSymbols({
181
+ knownEnums: new Set(["ECategory"]),
182
+ });
183
+
184
+ const mockCtx = { getText: () => "global.ECategory.CAT_A" };
185
+ expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("ECategory");
186
+ });
187
+
188
+ it("resolves this.variable pattern for enum-typed scope member", () => {
189
+ CodeGenState.currentScope = "Motor";
190
+ CodeGenState.symbols = createMockSymbols({
191
+ knownEnums: new Set(["Motor_State"]),
192
+ });
193
+ CodeGenState.typeRegistry.set("Motor_current", {
194
+ baseType: "Motor_State",
195
+ bitWidth: 0,
196
+ isArray: false,
197
+ isConst: false,
198
+ isEnum: true,
199
+ enumTypeName: "Motor_State",
200
+ });
201
+
202
+ const mockCtx = { getText: () => "this.current" };
203
+ expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("Motor_State");
204
+ });
205
+ });
206
+
207
+ describe("resolve() - TypeResolver fallback for struct member chains", () => {
208
+ /**
209
+ * Helper to build a mock ExpressionContext that contains a full postfix
210
+ * expression tree: global.input.assignedValue
211
+ */
212
+ const buildStructChainCtx = (
213
+ primaryToken: "GLOBAL" | "THIS" | "IDENTIFIER",
214
+ primaryText: string,
215
+ suffixes: string[],
216
+ ) => {
217
+ const primary = {
218
+ IDENTIFIER: () =>
219
+ primaryToken === "IDENTIFIER" ? { getText: () => primaryText } : null,
220
+ GLOBAL: () =>
221
+ primaryToken === "GLOBAL" ? { getText: () => "global" } : null,
222
+ THIS: () =>
223
+ primaryToken === "THIS" ? { getText: () => "this" } : null,
224
+ literal: () => null,
225
+ expression: () => null,
226
+ castExpression: () => null,
227
+ };
228
+ const children = [
229
+ { getText: () => primaryText },
230
+ ...suffixes.map((s) => ({ getText: () => s })),
231
+ ];
232
+ const postfix = { primaryExpression: () => primary, children };
233
+
234
+ // Build the full expression tree wrapping the postfix
235
+ const unary = {
236
+ postfixExpression: () => postfix,
237
+ unaryExpression: () => null,
238
+ };
239
+ const mult = { unaryExpression: () => [unary] };
240
+ const add = { multiplicativeExpression: () => [mult] };
241
+ const shift = { additiveExpression: () => [add] };
242
+ const bitAnd = { shiftExpression: () => [shift] };
243
+ const bitXor = { bitwiseAndExpression: () => [bitAnd] };
244
+ const bitOr = { bitwiseXorExpression: () => [bitXor] };
245
+ const rel = { bitwiseOrExpression: () => [bitOr] };
246
+ const eq = { relationalExpression: () => [rel] };
247
+ const and = { equalityExpression: () => [eq] };
248
+ const or = { andExpression: () => [and] };
249
+ const ternary = { orExpression: () => [or] };
250
+
251
+ return {
252
+ getText: () => primaryText + suffixes.join(""),
253
+ ternaryExpression: () => ternary,
254
+ } as never;
255
+ };
256
+
257
+ it("resolves global.struct.enumField via TypeResolver fallback", () => {
258
+ const symbolTable = new SymbolTable();
259
+ symbolTable.addStructField("TInput", "assignedValue", "EValueId");
260
+ CodeGenState.symbolTable = symbolTable;
261
+ CodeGenState.symbols = createMockSymbols({
262
+ knownEnums: new Set(["EValueId"]),
263
+ });
264
+ CodeGenState.typeRegistry.set("input", {
265
+ baseType: "TInput",
266
+ bitWidth: 0,
267
+ isArray: false,
268
+ isConst: false,
269
+ });
270
+
271
+ const ctx = buildStructChainCtx("GLOBAL", "global", [
272
+ ".input",
273
+ ".assignedValue",
274
+ ]);
275
+ expect(EnumTypeResolver.resolve(ctx)).toBe("EValueId");
276
+ });
277
+
278
+ it("returns null when struct field is not an enum type", () => {
279
+ const symbolTable = new SymbolTable();
280
+ symbolTable.addStructField("TInput", "count", "u32");
281
+ CodeGenState.symbolTable = symbolTable;
282
+ CodeGenState.symbols = createMockSymbols();
283
+ CodeGenState.typeRegistry.set("input", {
284
+ baseType: "TInput",
285
+ bitWidth: 0,
286
+ isArray: false,
287
+ isConst: false,
288
+ });
289
+
290
+ const ctx = buildStructChainCtx("GLOBAL", "global", [".input", ".count"]);
291
+ expect(EnumTypeResolver.resolve(ctx)).toBeNull();
292
+ });
293
+
294
+ it("returns null for RelationalExpressionContext (no ternaryExpression)", () => {
295
+ CodeGenState.symbols = createMockSymbols();
296
+ // RelationalExpressionContext doesn't have ternaryExpression
297
+ const ctx = { getText: () => "something.weird" } as never;
298
+ expect(EnumTypeResolver.resolve(ctx)).toBeNull();
299
+ });
300
+ });
301
+
302
+ describe("resolve() - edge cases", () => {
303
+ it("returns null for this.Enum.MEMBER when not in a scope", () => {
304
+ CodeGenState.currentScope = null;
305
+ CodeGenState.symbols = createMockSymbols({
306
+ knownEnums: new Set(["Motor_State"]),
307
+ });
308
+
309
+ const mockCtx = { getText: () => "this.State.IDLE" };
310
+ expect(EnumTypeResolver.resolve(mockCtx as never)).toBeNull();
311
+ });
312
+
313
+ it("returns null for this.variable when not in a scope", () => {
314
+ CodeGenState.currentScope = null;
315
+
316
+ const mockCtx = { getText: () => "this.current" };
317
+ expect(EnumTypeResolver.resolve(mockCtx as never)).toBeNull();
318
+ });
319
+
320
+ it("returns null for unknown enum in scoped pattern", () => {
321
+ CodeGenState.symbols = createMockSymbols({
322
+ knownEnums: new Set(), // No enums
323
+ });
324
+
325
+ const mockCtx = { getText: () => "Motor.State.IDLE" };
326
+ expect(EnumTypeResolver.resolve(mockCtx as never)).toBeNull();
327
+ });
328
+
329
+ it("returns null for single identifier that is not in type registry", () => {
330
+ CodeGenState.symbols = createMockSymbols();
331
+
332
+ const mockCtx = { getText: () => "unknownVar" };
333
+ expect(EnumTypeResolver.resolve(mockCtx as never)).toBeNull();
334
+ });
335
+ });
336
+ });