c-next 0.1.62 → 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 (55) hide show
  1. package/README.md +86 -63
  2. package/package.json +1 -1
  3. package/src/transpiler/Transpiler.ts +3 -2
  4. package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
  5. package/src/transpiler/__tests__/Transpiler.coverage.test.ts +1 -1
  6. package/src/transpiler/__tests__/Transpiler.test.ts +0 -23
  7. package/src/transpiler/logic/symbols/cnext/collectors/StructCollector.ts +156 -70
  8. package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +31 -6
  9. package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +43 -11
  10. package/src/transpiler/output/codegen/CodeGenState.ts +811 -0
  11. package/src/transpiler/output/codegen/CodeGenerator.ts +817 -1377
  12. package/src/transpiler/output/codegen/TypeResolver.ts +193 -149
  13. package/src/transpiler/output/codegen/TypeValidator.ts +148 -370
  14. package/src/transpiler/output/codegen/__tests__/CodeGenState.test.ts +446 -0
  15. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +326 -60
  16. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +1 -1
  17. package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +435 -196
  18. package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +51 -67
  19. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +495 -471
  20. package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +39 -43
  21. package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +52 -55
  22. package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +122 -62
  23. package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +101 -144
  24. package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +143 -126
  25. package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +287 -320
  26. package/src/transpiler/output/codegen/generators/GeneratorRegistry.ts +12 -0
  27. package/src/transpiler/output/codegen/generators/__tests__/GeneratorRegistry.test.ts +28 -1
  28. package/src/transpiler/output/codegen/generators/declarationGenerators/ArrayDimensionUtils.ts +67 -0
  29. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +121 -51
  30. package/src/transpiler/output/codegen/generators/declarationGenerators/StructGenerator.ts +100 -23
  31. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ArrayDimensionUtils.test.ts +125 -0
  32. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +157 -4
  33. package/src/transpiler/output/codegen/generators/support/HelperGenerator.ts +23 -22
  34. package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +54 -61
  35. package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +21 -30
  36. package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +56 -53
  37. package/src/transpiler/output/codegen/helpers/CppModeHelper.ts +22 -30
  38. package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +108 -50
  39. package/src/transpiler/output/codegen/helpers/FloatBitHelper.ts +16 -31
  40. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +103 -96
  41. package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +9 -0
  42. package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +58 -103
  43. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +97 -40
  44. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +223 -128
  45. package/src/transpiler/output/codegen/helpers/__tests__/CppModeHelper.test.ts +68 -41
  46. package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +198 -47
  47. package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +39 -37
  48. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +191 -453
  49. package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +229 -0
  50. package/src/transpiler/output/codegen/resolution/ScopeResolver.ts +60 -0
  51. package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +177 -0
  52. package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +336 -0
  53. package/src/transpiler/output/codegen/resolution/__tests__/SizeofResolver.test.ts +201 -0
  54. package/src/transpiler/output/codegen/types/ITypeResolverDeps.ts +0 -23
  55. package/src/transpiler/output/codegen/types/ITypeValidatorDeps.ts +0 -53
@@ -10,33 +10,21 @@
10
10
  * - Array bounds checking
11
11
  * - Read-only register members
12
12
  * - Callback field assignments
13
+ *
14
+ * Migrated to use CodeGenState instead of constructor DI.
13
15
  */
14
16
 
15
17
  import * as Parser from "../../../logic/parser/grammar/CNextParser.js";
16
18
  import TypeValidator from "../TypeValidator.js";
17
19
  import EnumAssignmentValidator from "./EnumAssignmentValidator.js";
18
- import TTypeInfo from "../types/TTypeInfo.js";
20
+ import CodeGenState from "../CodeGenState.js";
21
+ import TypeCheckUtils from "../../../../utils/TypeCheckUtils.js";
19
22
 
20
23
  /**
21
- * Dependencies required for assignment validation.
24
+ * Callbacks required for assignment validation.
25
+ * These need CodeGenerator context and cannot be replaced with static state.
22
26
  */
23
- interface IAssignmentValidatorDeps {
24
- /** TypeValidator for const and callback validation */
25
- readonly typeValidator: TypeValidator;
26
- /** EnumAssignmentValidator for enum type validation */
27
- readonly enumValidator: EnumAssignmentValidator;
28
- /** Type registry for looking up variable types */
29
- readonly typeRegistry: ReadonlyMap<string, TTypeInfo>;
30
- /** Float shadow tracking state for invalidation */
31
- readonly floatShadowCurrent: Set<string>;
32
- /** Register member access modifiers for read-only checks */
33
- readonly registerMemberAccess: ReadonlyMap<string, string>;
34
- /** Callback field types for nominal typing validation */
35
- readonly callbackFieldTypes: ReadonlyMap<string, string>;
36
- /** Check if a type is a known struct */
37
- isKnownStruct: (typeName: string) => boolean;
38
- /** Check if a type is an integer type */
39
- isIntegerType: (typeName: string) => boolean;
27
+ interface IAssignmentValidatorCallbacks {
40
28
  /** Get the type of an expression */
41
29
  getExpressionType: (ctx: Parser.ExpressionContext) => string | null;
42
30
  /** Try to evaluate a constant expression */
@@ -49,12 +37,6 @@ interface IAssignmentValidatorDeps {
49
37
  * Coordinates all assignment validations.
50
38
  */
51
39
  class AssignmentValidator {
52
- private readonly deps: IAssignmentValidatorDeps;
53
-
54
- constructor(deps: IAssignmentValidatorDeps) {
55
- this.deps = deps;
56
- }
57
-
58
40
  /**
59
41
  * Validate an assignment target.
60
42
  *
@@ -62,19 +44,26 @@ class AssignmentValidator {
62
44
  * @param expression - The expression being assigned
63
45
  * @param isCompound - Whether this is a compound assignment (+<-, -<-, etc.)
64
46
  * @param line - Line number for error messages
47
+ * @param callbacks - Callbacks to CodeGenerator methods
65
48
  */
66
- validate(
49
+ static validate(
67
50
  targetCtx: Parser.AssignmentTargetContext,
68
51
  expression: Parser.ExpressionContext,
69
52
  isCompound: boolean,
70
53
  line: number,
54
+ callbacks: IAssignmentValidatorCallbacks,
71
55
  ): void {
72
56
  const postfixOps = targetCtx.postfixTargetOp();
73
57
  const baseId = targetCtx.IDENTIFIER()?.getText();
74
58
 
75
59
  // Case 1: Simple identifier assignment (no postfix ops)
76
60
  if (baseId && postfixOps.length === 0) {
77
- this.validateSimpleIdentifier(baseId, expression, isCompound);
61
+ AssignmentValidator.validateSimpleIdentifier(
62
+ baseId,
63
+ expression,
64
+ isCompound,
65
+ callbacks,
66
+ );
78
67
  return;
79
68
  }
80
69
 
@@ -94,63 +83,75 @@ class AssignmentValidator {
94
83
 
95
84
  // Case 2: Has subscripts - validate array bounds
96
85
  if (subscriptExprs.length > 0 && identifiers.length > 0) {
97
- this.validateArrayElement(identifiers[0], subscriptExprs, line);
86
+ AssignmentValidator.validateArrayElement(
87
+ identifiers[0],
88
+ subscriptExprs,
89
+ line,
90
+ callbacks,
91
+ );
98
92
  }
99
93
 
100
94
  // Case 3: Has member access - validate member access
101
95
  if (identifiers.length >= 2) {
102
- this.validateMemberAccess(identifiers, expression);
96
+ AssignmentValidator.validateMemberAccess(
97
+ identifiers,
98
+ expression,
99
+ callbacks,
100
+ );
103
101
  }
104
102
  }
105
103
 
106
104
  /**
107
105
  * Validate simple identifier assignment.
108
106
  */
109
- private validateSimpleIdentifier(
107
+ private static validateSimpleIdentifier(
110
108
  id: string,
111
109
  expression: Parser.ExpressionContext,
112
110
  isCompound: boolean,
111
+ callbacks: IAssignmentValidatorCallbacks,
113
112
  ): void {
114
113
  // ADR-013: Validate const assignment
115
- const constError = this.deps.typeValidator.checkConstAssignment(id);
114
+ const constError = TypeValidator.checkConstAssignment(id);
116
115
  if (constError) {
117
116
  throw new Error(constError);
118
117
  }
119
118
 
120
119
  // Invalidate float shadow when variable is assigned directly
121
120
  const shadowName = `__bits_${id}`;
122
- this.deps.floatShadowCurrent.delete(shadowName);
121
+ CodeGenState.floatShadowCurrent.delete(shadowName);
123
122
 
124
- const targetTypeInfo = this.deps.typeRegistry.get(id);
123
+ const targetTypeInfo = CodeGenState.typeRegistry.get(id);
125
124
  if (!targetTypeInfo) {
126
125
  return;
127
126
  }
128
127
 
129
- // ADR-017: Validate enum type assignment
128
+ // ADR-017: Validate enum assignment for enum-typed variable
130
129
  if (targetTypeInfo.isEnum && targetTypeInfo.enumTypeName) {
131
- this.deps.enumValidator.validateEnumAssignment(
130
+ EnumAssignmentValidator.validateEnumAssignment(
132
131
  targetTypeInfo.enumTypeName,
133
132
  expression,
134
133
  );
135
134
  }
136
135
 
137
136
  // ADR-024: Validate integer type conversions
138
- if (this.deps.isIntegerType(targetTypeInfo.baseType)) {
137
+ if (TypeCheckUtils.isInteger(targetTypeInfo.baseType)) {
139
138
  try {
140
- this.deps.typeValidator.validateIntegerAssignment(
139
+ TypeValidator.validateIntegerAssignment(
141
140
  targetTypeInfo.baseType,
142
141
  expression.getText(),
143
- this.deps.getExpressionType(expression),
142
+ callbacks.getExpressionType(expression),
144
143
  isCompound,
145
144
  );
146
145
  } catch (validationError) {
147
- const line = expression.start?.line ?? 0;
146
+ const errorLine = expression.start?.line ?? 0;
148
147
  const col = expression.start?.column ?? 0;
149
148
  const msg =
150
149
  validationError instanceof Error
151
150
  ? validationError.message
152
151
  : String(validationError);
153
- throw new Error(`${line}:${col} ${msg}`, { cause: validationError });
152
+ throw new Error(`${errorLine}:${col} ${msg}`, {
153
+ cause: validationError,
154
+ });
154
155
  }
155
156
  }
156
157
  }
@@ -158,26 +159,27 @@ class AssignmentValidator {
158
159
  /**
159
160
  * Validate array element assignment.
160
161
  */
161
- private validateArrayElement(
162
+ private static validateArrayElement(
162
163
  arrayName: string,
163
164
  subscriptExprs: Parser.ExpressionContext[],
164
165
  line: number,
166
+ callbacks: IAssignmentValidatorCallbacks,
165
167
  ): void {
166
168
  // ADR-013: Validate const assignment on array
167
- const constError = this.deps.typeValidator.checkConstAssignment(arrayName);
169
+ const constError = TypeValidator.checkConstAssignment(arrayName);
168
170
  if (constError) {
169
171
  throw new Error(`${constError} (array element)`);
170
172
  }
171
173
 
172
174
  // ADR-036: Compile-time bounds checking
173
- const typeInfo = this.deps.typeRegistry.get(arrayName);
175
+ const typeInfo = CodeGenState.typeRegistry.get(arrayName);
174
176
  if (typeInfo?.isArray && typeInfo.arrayDimensions) {
175
- this.deps.typeValidator.checkArrayBounds(
177
+ TypeValidator.checkArrayBounds(
176
178
  arrayName,
177
179
  typeInfo.arrayDimensions,
178
180
  subscriptExprs,
179
181
  line,
180
- this.deps.tryEvaluateConstant,
182
+ callbacks.tryEvaluateConstant,
181
183
  );
182
184
  }
183
185
  }
@@ -185,9 +187,10 @@ class AssignmentValidator {
185
187
  /**
186
188
  * Validate member access assignment.
187
189
  */
188
- private validateMemberAccess(
190
+ private static validateMemberAccess(
189
191
  identifiers: string[],
190
192
  expression: Parser.ExpressionContext,
193
+ callbacks: IAssignmentValidatorCallbacks,
191
194
  ): void {
192
195
  if (identifiers.length < 2) {
193
196
  return;
@@ -197,7 +200,7 @@ class AssignmentValidator {
197
200
  const memberName = identifiers[1];
198
201
 
199
202
  // ADR-013: Validate const assignment on struct root
200
- const constError = this.deps.typeValidator.checkConstAssignment(rootName);
203
+ const constError = TypeValidator.checkConstAssignment(rootName);
201
204
  if (constError) {
202
205
  throw new Error(`${constError} (member access)`);
203
206
  }
@@ -205,7 +208,7 @@ class AssignmentValidator {
205
208
  const fullName = `${rootName}_${memberName}`;
206
209
 
207
210
  // ADR-013: Check for read-only register members
208
- const accessMod = this.deps.registerMemberAccess.get(fullName);
211
+ const accessMod = CodeGenState.symbols?.registerMemberAccess.get(fullName);
209
212
  if (accessMod === "ro") {
210
213
  throw new Error(
211
214
  `cannot assign to read-only register member '${memberName}' ` +
@@ -214,19 +217,19 @@ class AssignmentValidator {
214
217
  }
215
218
 
216
219
  // ADR-029: Validate callback field assignments with nominal typing
217
- const rootTypeInfo = this.deps.typeRegistry.get(rootName);
218
- if (rootTypeInfo && this.deps.isKnownStruct(rootTypeInfo.baseType)) {
220
+ const rootTypeInfo = CodeGenState.typeRegistry.get(rootName);
221
+ if (rootTypeInfo && CodeGenState.isKnownStruct(rootTypeInfo.baseType)) {
219
222
  const structType = rootTypeInfo.baseType;
220
223
  const callbackFieldKey = `${structType}.${memberName}`;
221
224
  const expectedCallbackType =
222
- this.deps.callbackFieldTypes.get(callbackFieldKey);
225
+ CodeGenState.callbackFieldTypes.get(callbackFieldKey);
223
226
 
224
227
  if (expectedCallbackType) {
225
- this.deps.typeValidator.validateCallbackAssignment(
228
+ TypeValidator.validateCallbackAssignment(
226
229
  expectedCallbackType,
227
230
  expression,
228
231
  memberName,
229
- this.deps.isCallbackTypeUsedAsFieldType,
232
+ callbacks.isCallbackTypeUsedAsFieldType,
230
233
  );
231
234
  }
232
235
  }
@@ -5,26 +5,16 @@
5
5
  *
6
6
  * In C mode, struct parameters are passed by pointer (need & for address, * for type).
7
7
  * In C++ mode, struct parameters are passed by reference (no & needed, & for type).
8
+ *
9
+ * Migrated to use CodeGenState instead of constructor DI.
8
10
  */
9
11
 
10
- /**
11
- * Options for C/C++ mode helpers.
12
- */
13
- interface CppModeOptions {
14
- /** Whether we're generating C++ code */
15
- cppMode: boolean;
16
- }
12
+ import CodeGenState from "../CodeGenState";
17
13
 
18
14
  /**
19
- * Helper class for C/C++ mode-specific code generation patterns.
15
+ * Static helper class for C/C++ mode-specific code generation patterns.
20
16
  */
21
17
  class CppModeHelper {
22
- private readonly cppMode: boolean;
23
-
24
- constructor(options: CppModeOptions) {
25
- this.cppMode = options.cppMode;
26
- }
27
-
28
18
  /**
29
19
  * Get address-of expression for struct parameter passing.
30
20
  * C mode: `&expr` (pass pointer to struct)
@@ -33,8 +23,8 @@ class CppModeHelper {
33
23
  * @param expr - The expression to potentially wrap
34
24
  * @returns The expression with address-of operator in C mode
35
25
  */
36
- maybeAddressOf(expr: string): string {
37
- return this.cppMode ? expr : `&${expr}`;
26
+ static maybeAddressOf(expr: string): string {
27
+ return CodeGenState.cppMode ? expr : `&${expr}`;
38
28
  }
39
29
 
40
30
  /**
@@ -45,8 +35,8 @@ class CppModeHelper {
45
35
  * @param expr - The expression to potentially dereference
46
36
  * @returns The expression with dereference in C mode
47
37
  */
48
- maybeDereference(expr: string): string {
49
- return this.cppMode ? expr : `(*${expr})`;
38
+ static maybeDereference(expr: string): string {
39
+ return CodeGenState.cppMode ? expr : `(*${expr})`;
50
40
  }
51
41
 
52
42
  /**
@@ -56,8 +46,8 @@ class CppModeHelper {
56
46
  *
57
47
  * @returns The type modifier character
58
48
  */
59
- refOrPtr(): string {
60
- return this.cppMode ? "&" : "*";
49
+ static refOrPtr(): string {
50
+ return CodeGenState.cppMode ? "&" : "*";
61
51
  }
62
52
 
63
53
  /**
@@ -67,8 +57,8 @@ class CppModeHelper {
67
57
  *
68
58
  * @returns The member access separator
69
59
  */
70
- memberSeparator(): string {
71
- return this.cppMode ? "." : "->";
60
+ static memberSeparator(): string {
61
+ return CodeGenState.cppMode ? "." : "->";
72
62
  }
73
63
 
74
64
  /**
@@ -78,8 +68,8 @@ class CppModeHelper {
78
68
  *
79
69
  * @returns The null pointer literal
80
70
  */
81
- nullLiteral(): string {
82
- return this.cppMode ? "nullptr" : "NULL";
71
+ static nullLiteral(): string {
72
+ return CodeGenState.cppMode ? "nullptr" : "NULL";
83
73
  }
84
74
 
85
75
  /**
@@ -91,8 +81,10 @@ class CppModeHelper {
91
81
  * @param expr - The expression to cast
92
82
  * @returns The cast expression
93
83
  */
94
- cast(type: string, expr: string): string {
95
- return this.cppMode ? `static_cast<${type}>(${expr})` : `(${type})${expr}`;
84
+ static cast(type: string, expr: string): string {
85
+ return CodeGenState.cppMode
86
+ ? `static_cast<${type}>(${expr})`
87
+ : `(${type})${expr}`;
96
88
  }
97
89
 
98
90
  /**
@@ -104,8 +96,8 @@ class CppModeHelper {
104
96
  * @param expr - The expression to cast
105
97
  * @returns The cast expression
106
98
  */
107
- reinterpretCast(type: string, expr: string): string {
108
- return this.cppMode
99
+ static reinterpretCast(type: string, expr: string): string {
100
+ return CodeGenState.cppMode
109
101
  ? `reinterpret_cast<${type}>(${expr})`
110
102
  : `(${type})${expr}`;
111
103
  }
@@ -115,8 +107,8 @@ class CppModeHelper {
115
107
  *
116
108
  * @returns True if generating C++ code
117
109
  */
118
- isCppMode(): boolean {
119
- return this.cppMode;
110
+ static isCppMode(): boolean {
111
+ return CodeGenState.cppMode;
120
112
  }
121
113
  }
122
114
 
@@ -2,6 +2,7 @@
2
2
  * EnumAssignmentValidator - Validates enum type assignments
3
3
  *
4
4
  * Issue #644: Extracted from CodeGenerator to reduce file size.
5
+ * Static class using CodeGenState for all state access.
5
6
  *
6
7
  * Validates that enum assignments are type-safe:
7
8
  * - Cannot assign different enum types to each other
@@ -11,31 +12,15 @@
11
12
  */
12
13
 
13
14
  import * as Parser from "../../../logic/parser/grammar/CNextParser.js";
14
-
15
- /**
16
- * Dependencies required for enum validation.
17
- */
18
- interface IEnumValidatorDeps {
19
- /** Set of known enum type names */
20
- knownEnums: ReadonlySet<string>;
21
- /** Get the current scope name (for this. prefix handling) */
22
- getCurrentScope: () => string | null;
23
- /** Get the enum type of an expression */
24
- getExpressionEnumType: (ctx: Parser.ExpressionContext) => string | null;
25
- /** Check if expression is an integer literal or variable */
26
- isIntegerExpression: (ctx: Parser.ExpressionContext) => boolean;
27
- }
15
+ import CodeGenState from "../CodeGenState.js";
16
+ import EnumTypeResolver from "../resolution/EnumTypeResolver.js";
17
+ import TypeCheckUtils from "../../../../utils/TypeCheckUtils.js";
28
18
 
29
19
  /**
30
20
  * Validates enum type assignments.
21
+ * All methods are static - uses CodeGenState for state access.
31
22
  */
32
23
  class EnumAssignmentValidator {
33
- private readonly deps: IEnumValidatorDeps;
34
-
35
- constructor(deps: IEnumValidatorDeps) {
36
- this.deps = deps;
37
- }
38
-
39
24
  /**
40
25
  * Validate that an expression can be assigned to an enum-typed variable.
41
26
  * Throws an error if the assignment is invalid.
@@ -43,16 +28,16 @@ class EnumAssignmentValidator {
43
28
  * @param typeName - The target enum type name
44
29
  * @param expression - The expression being assigned
45
30
  */
46
- validateEnumAssignment(
31
+ static validateEnumAssignment(
47
32
  typeName: string,
48
33
  expression: Parser.ExpressionContext,
49
34
  ): void {
50
35
  // Only validate if the target type is a known enum
51
- if (!this.deps.knownEnums.has(typeName)) {
36
+ if (!CodeGenState.isKnownEnum(typeName)) {
52
37
  return;
53
38
  }
54
39
 
55
- const valueEnumType = this.deps.getExpressionEnumType(expression);
40
+ const valueEnumType = EnumTypeResolver.resolve(expression);
56
41
 
57
42
  // Check if assigning from a different enum type
58
43
  if (valueEnumType && valueEnumType !== typeName) {
@@ -62,53 +47,74 @@ class EnumAssignmentValidator {
62
47
  }
63
48
 
64
49
  // Check if assigning integer to enum
65
- if (this.deps.isIntegerExpression(expression)) {
50
+ if (EnumAssignmentValidator.isIntegerExpression(expression)) {
66
51
  throw new Error(`Error: Cannot assign integer to ${typeName} enum`);
67
52
  }
68
53
 
69
54
  // Check if assigning a non-enum, non-integer expression
70
55
  if (!valueEnumType) {
71
56
  const exprText = expression.getText();
72
- this.validateNonEnumExpression(exprText, typeName);
57
+ EnumAssignmentValidator.validateNonEnumExpression(exprText, typeName);
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Check if an expression is an integer literal or integer-typed variable.
63
+ * Used to detect comparisons between enums and integers.
64
+ */
65
+ static isIntegerExpression(
66
+ ctx: Parser.ExpressionContext | Parser.RelationalExpressionContext,
67
+ ): boolean {
68
+ const text = ctx.getText();
69
+
70
+ // Check for integer literals
71
+ if (
72
+ /^-?\d+$/.exec(text) ||
73
+ /^0[xX][0-9a-fA-F]+$/.exec(text) ||
74
+ /^0[bB][01]+$/.exec(text)
75
+ ) {
76
+ return true;
77
+ }
78
+
79
+ // Check if it's a variable of primitive integer type
80
+ if (/^[a-zA-Z_]\w*$/.exec(text)) {
81
+ const typeInfo = CodeGenState.typeRegistry.get(text);
82
+ if (
83
+ typeInfo &&
84
+ !typeInfo.isEnum &&
85
+ TypeCheckUtils.isInteger(typeInfo.baseType)
86
+ ) {
87
+ return true;
88
+ }
73
89
  }
90
+
91
+ return false;
74
92
  }
75
93
 
76
94
  /**
77
95
  * Validate a non-enum expression being assigned to an enum type.
78
96
  * Handles various access patterns like this.Enum.MEMBER, global.Enum.MEMBER, etc.
79
97
  */
80
- private validateNonEnumExpression(exprText: string, typeName: string): void {
98
+ private static validateNonEnumExpression(
99
+ exprText: string,
100
+ typeName: string,
101
+ ): void {
81
102
  const parts = exprText.split(".");
82
- const currentScope = this.deps.getCurrentScope();
83
103
 
84
104
  // ADR-016: Handle this.State.MEMBER pattern
85
- if (parts[0] === "this" && currentScope && parts.length >= 3) {
86
- const scopedEnumName = `${currentScope}_${parts[1]}`;
87
- if (scopedEnumName === typeName) {
88
- // Valid this.Enum.Member access
89
- return;
90
- }
91
- throw new Error(
92
- `Error: Cannot assign non-enum value to ${typeName} enum`,
93
- );
105
+ if (parts[0] === "this" && parts.length >= 3) {
106
+ EnumAssignmentValidator.validateThisEnumPattern(parts, typeName);
107
+ return;
94
108
  }
95
109
 
96
- // Issue #478: Handle global.Enum.MEMBER pattern
110
+ // Issue #478: Handle global.Enum.MEMBER or global.struct.field pattern
97
111
  if (parts[0] === "global" && parts.length >= 3) {
98
- // global.ECategory.CAT_A -> ECategory
99
- const globalEnumName = parts[1];
100
- if (globalEnumName === typeName) {
101
- // Valid global.Enum.Member access
102
- return;
103
- }
104
- throw new Error(
105
- `Error: Cannot assign non-enum value to ${typeName} enum`,
106
- );
112
+ EnumAssignmentValidator.validateGlobalEnumPattern(parts, typeName);
113
+ return;
107
114
  }
108
115
 
109
116
  // Allow if it's an enum member access of the correct type
110
117
  if (exprText.startsWith(typeName + ".")) {
111
- // Direct enum member access: EnumType.MEMBER
112
118
  return;
113
119
  }
114
120
 
@@ -124,17 +130,69 @@ class EnumAssignmentValidator {
124
130
  }
125
131
 
126
132
  // parts.length === 2: Could be Enum.Member or variable.field
127
- // Allow if it's a known enum type (even if different - getExpressionEnumType would catch mismatches)
133
+ // Allow if it's a known enum type (even if different - resolve would catch mismatches)
128
134
  if (
129
135
  parts.length === 2 &&
130
- parts[0] !== typeName &&
131
- !this.deps.knownEnums.has(parts[0])
136
+ !EnumAssignmentValidator.isMatchingEnum(parts[0], typeName)
132
137
  ) {
133
138
  throw new Error(
134
139
  `Error: Cannot assign non-enum value to ${typeName} enum`,
135
140
  );
136
141
  }
137
142
  }
143
+
144
+ /**
145
+ * ADR-016: Validate this.Enum.MEMBER pattern inside a scope.
146
+ */
147
+ private static validateThisEnumPattern(
148
+ parts: string[],
149
+ typeName: string,
150
+ ): void {
151
+ if (!CodeGenState.currentScope) {
152
+ throw new Error(
153
+ `Error: Cannot assign non-enum value to ${typeName} enum`,
154
+ );
155
+ }
156
+ const scopedEnumName = `${CodeGenState.currentScope}_${parts[1]}`;
157
+ if (scopedEnumName !== typeName) {
158
+ throw new Error(
159
+ `Error: Cannot assign non-enum value to ${typeName} enum`,
160
+ );
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Issue #478: Validate global.X.Y pattern.
166
+ * If X is a known enum, validates it matches the target type.
167
+ * If X is not an enum (e.g. struct variable), allows through since
168
+ * EnumTypeResolver.resolve() with TypeResolver fallback handles the chain.
169
+ */
170
+ private static validateGlobalEnumPattern(
171
+ parts: string[],
172
+ typeName: string,
173
+ ): void {
174
+ const name = parts[1];
175
+
176
+ // Not an enum (e.g. struct variable like global.input.field) — allow through
177
+ // since EnumTypeResolver.resolve() with TypeResolver fallback handles the chain
178
+ if (!CodeGenState.isKnownEnum(name)) {
179
+ return;
180
+ }
181
+
182
+ // Known enum that doesn't match target type
183
+ if (name !== typeName) {
184
+ throw new Error(
185
+ `Error: Cannot assign non-enum value to ${typeName} enum`,
186
+ );
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Check if an identifier is the target enum type or a known enum type.
192
+ */
193
+ private static isMatchingEnum(identifier: string, typeName: string): boolean {
194
+ return identifier === typeName || CodeGenState.isKnownEnum(identifier);
195
+ }
138
196
  }
139
197
 
140
198
  export default EnumAssignmentValidator;