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,129 @@
1
+ /**
2
+ * Helper utilities for generating array access code.
3
+ * Extracted from CodeGenerator to improve testability.
4
+ */
5
+ import IArrayAccessInfo from "../types/IArrayAccessInfo";
6
+ import IArrayAccessDeps from "../types/IArrayAccessDeps";
7
+ import BitRangeHelper from "./BitRangeHelper";
8
+ import CodeGenErrors from "./CodeGenErrors";
9
+
10
+ /**
11
+ * Helper class for array access code generation.
12
+ * Works with an intermediate representation (IArrayAccessInfo)
13
+ * instead of ANTLR parser contexts for testability.
14
+ */
15
+ class ArrayAccessHelper {
16
+ /**
17
+ * Generate the complete array access expression.
18
+ * Routes to single-index or bit-range based on accessType.
19
+ */
20
+ static generate(info: IArrayAccessInfo, deps: IArrayAccessDeps): string {
21
+ if (info.accessType === "single-index") {
22
+ return ArrayAccessHelper.generateSingleIndex(info);
23
+ }
24
+ return ArrayAccessHelper.generateBitRange(info, deps);
25
+ }
26
+
27
+ /**
28
+ * Generate single-index access: array[i]
29
+ * Validates that bitmap types don't use bracket notation.
30
+ */
31
+ static generateSingleIndex(info: IArrayAccessInfo): string {
32
+ ArrayAccessHelper.validateNotBitmap(info);
33
+ return `${info.resolvedName}[${info.indexExpr}]`;
34
+ }
35
+
36
+ /**
37
+ * Validate bitmap access is not using bracket notation.
38
+ * Bitmaps must use named field access (e.g., flags.FIELD_NAME).
39
+ * @throws Error if attempting bracket indexing on a bitmap type
40
+ */
41
+ static validateNotBitmap(info: IArrayAccessInfo): void {
42
+ if (info.typeInfo?.isBitmap && info.typeInfo.bitmapTypeName) {
43
+ throw CodeGenErrors.bitmapBracketIndexing(
44
+ info.line,
45
+ info.typeInfo.bitmapTypeName,
46
+ info.rawName,
47
+ );
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Generate bit range access: value[start, width]
53
+ * Handles both integer and float types.
54
+ */
55
+ static generateBitRange(
56
+ info: IArrayAccessInfo,
57
+ deps: IArrayAccessDeps,
58
+ ): string {
59
+ if (ArrayAccessHelper.isFloatBitRange(info.typeInfo)) {
60
+ return ArrayAccessHelper.generateFloatBitRange(info, deps);
61
+ }
62
+ return ArrayAccessHelper.generateIntegerBitRange(info, deps);
63
+ }
64
+
65
+ /**
66
+ * Determine if this is a float bit range (needs shadow variable).
67
+ */
68
+ static isFloatBitRange(typeInfo: { baseType?: string } | undefined): boolean {
69
+ return typeInfo?.baseType === "f32" || typeInfo?.baseType === "f64";
70
+ }
71
+
72
+ /**
73
+ * Generate float bit range read with shadow variable.
74
+ * Uses memcpy pattern to safely reinterpret float bits.
75
+ */
76
+ static generateFloatBitRange(
77
+ info: IArrayAccessInfo,
78
+ deps: IArrayAccessDeps,
79
+ ): string {
80
+ if (!deps.isInFunctionBody()) {
81
+ throw CodeGenErrors.floatBitIndexingAtGlobalScope(
82
+ info.rawName,
83
+ info.startExpr ?? "0",
84
+ info.widthExpr ?? "0",
85
+ );
86
+ }
87
+
88
+ deps.requireInclude("string");
89
+ deps.requireInclude("float_static_assert");
90
+
91
+ const isF64 = info.typeInfo?.baseType === "f64";
92
+ const shadowType = BitRangeHelper.getShadowType(
93
+ info.typeInfo?.baseType ?? "f32",
94
+ );
95
+ const shadowName = BitRangeHelper.getShadowVarName(info.rawName);
96
+ const mask = deps.generateBitMask(info.widthExpr ?? "0", isF64);
97
+
98
+ // Register shadow variable if not already declared
99
+ deps.registerFloatShadow(shadowName, shadowType);
100
+
101
+ const shadowIsCurrent = deps.isShadowCurrent(shadowName);
102
+ deps.markShadowCurrent(shadowName);
103
+
104
+ return BitRangeHelper.buildFloatBitReadExpr({
105
+ shadowName,
106
+ varName: info.resolvedName,
107
+ start: info.startExpr ?? "0",
108
+ mask,
109
+ shadowIsCurrent,
110
+ });
111
+ }
112
+
113
+ /**
114
+ * Generate integer bit range read: ((value >> start) & mask)
115
+ */
116
+ static generateIntegerBitRange(
117
+ info: IArrayAccessInfo,
118
+ deps: IArrayAccessDeps,
119
+ ): string {
120
+ const mask = deps.generateBitMask(info.widthExpr ?? "0");
121
+ return BitRangeHelper.buildIntegerBitReadExpr({
122
+ varName: info.resolvedName,
123
+ start: info.startExpr ?? "0",
124
+ mask,
125
+ });
126
+ }
127
+ }
128
+
129
+ export default ArrayAccessHelper;
@@ -7,18 +7,12 @@
7
7
  * - Array initializers with size inference: u8 data[] <- [1, 2, 3]
8
8
  * - Fill-all syntax: u8 data[10] <- [0*]
9
9
  * - Array size validation
10
+ *
11
+ * Migrated to use CodeGenState instead of constructor DI.
10
12
  */
11
13
 
12
14
  import * as Parser from "../../../logic/parser/grammar/CNextParser.js";
13
- import TTypeInfo from "../types/TTypeInfo.js";
14
-
15
- /**
16
- * Array initialization tracking state.
17
- */
18
- interface IArrayInitState {
19
- lastArrayInitCount: number;
20
- lastArrayFillValue: string | undefined;
21
- }
15
+ import CodeGenState from "../CodeGenState.js";
22
16
 
23
17
  /**
24
18
  * Result from processing array initialization.
@@ -33,18 +27,10 @@ interface IArrayInitResult {
33
27
  }
34
28
 
35
29
  /**
36
- * Dependencies required for array initialization.
30
+ * Callbacks required for array initialization.
31
+ * These need CodeGenerator context and cannot be replaced with static state.
37
32
  */
38
- interface IArrayInitHelperDeps {
39
- /** Type registry for updating inferred dimensions */
40
- typeRegistry: Map<string, TTypeInfo>;
41
- /** Local arrays set for tracking */
42
- localArrays: Set<string>;
43
- /** Array initialization tracking state */
44
- arrayInitState: IArrayInitState;
45
- /** Get/set expected type context */
46
- getExpectedType: () => string | null;
47
- setExpectedType: (type: string | null) => void;
33
+ interface IArrayInitCallbacks {
48
34
  /** Generate expression code */
49
35
  generateExpression: (ctx: Parser.ExpressionContext) => string;
50
36
  /** Get type name from type context */
@@ -57,12 +43,6 @@ interface IArrayInitHelperDeps {
57
43
  * Handles array initialization with size inference and fill-all syntax.
58
44
  */
59
45
  class ArrayInitHelper {
60
- private readonly deps: IArrayInitHelperDeps;
61
-
62
- constructor(deps: IArrayInitHelperDeps) {
63
- this.deps = deps;
64
- }
65
-
66
46
  /**
67
47
  * Process array initialization expression.
68
48
  * Returns null if not an array initializer pattern.
@@ -73,33 +53,46 @@ class ArrayInitHelper {
73
53
  * @param arrayDims - Array dimension contexts
74
54
  * @param hasEmptyArrayDim - Whether any dimension is empty (for inference)
75
55
  * @param declaredSize - First dimension size if explicit, null otherwise
56
+ * @param callbacks - Callbacks to CodeGenerator methods
76
57
  */
77
- processArrayInit(
58
+ static processArrayInit(
78
59
  name: string,
79
60
  typeCtx: Parser.TypeContext,
80
61
  expression: Parser.ExpressionContext,
81
62
  arrayDims: Parser.ArrayDimensionContext[],
82
63
  hasEmptyArrayDim: boolean,
83
64
  declaredSize: number | null,
65
+ callbacks: IArrayInitCallbacks,
84
66
  ): IArrayInitResult | null {
85
67
  // Reset and generate initializer
86
- this.deps.arrayInitState.lastArrayInitCount = 0;
87
- this.deps.arrayInitState.lastArrayFillValue = undefined;
68
+ CodeGenState.lastArrayInitCount = 0;
69
+ CodeGenState.lastArrayFillValue = undefined;
88
70
 
89
- const initValue = this._generateArrayInitValue(typeCtx, expression);
71
+ const initValue = ArrayInitHelper._generateArrayInitValue(
72
+ typeCtx,
73
+ expression,
74
+ callbacks,
75
+ );
90
76
 
91
77
  // Check if it was an array initializer
92
- if (!this._isArrayInitializer()) {
78
+ if (!ArrayInitHelper._isArrayInitializer()) {
93
79
  return null;
94
80
  }
95
81
 
96
- this.deps.localArrays.add(name);
82
+ CodeGenState.localArrays.add(name);
97
83
 
98
84
  const dimensionSuffix = hasEmptyArrayDim
99
- ? this._processSizeInference(name)
100
- : this._processExplicitSize(arrayDims, declaredSize);
101
-
102
- const finalInitValue = this._expandFillAllSyntax(initValue, declaredSize);
85
+ ? ArrayInitHelper._processSizeInference(name)
86
+ : ArrayInitHelper._processExplicitSize(
87
+ arrayDims,
88
+ declaredSize,
89
+ callbacks,
90
+ );
91
+
92
+ const finalInitValue = ArrayInitHelper._expandFillAllSyntax(
93
+ initValue,
94
+ declaredSize,
95
+ );
103
96
 
104
97
  return { isArrayInit: true, dimensionSuffix, initValue: finalInitValue };
105
98
  }
@@ -107,66 +100,66 @@ class ArrayInitHelper {
107
100
  /**
108
101
  * Generate the array initializer value with proper expected type
109
102
  */
110
- private _generateArrayInitValue(
103
+ private static _generateArrayInitValue(
111
104
  typeCtx: Parser.TypeContext,
112
105
  expression: Parser.ExpressionContext,
106
+ callbacks: IArrayInitCallbacks,
113
107
  ): string {
114
- const typeName = this.deps.getTypeName(typeCtx);
115
- const savedExpectedType = this.deps.getExpectedType();
116
- this.deps.setExpectedType(typeName);
117
- const initValue = this.deps.generateExpression(expression);
118
- this.deps.setExpectedType(savedExpectedType);
108
+ const typeName = callbacks.getTypeName(typeCtx);
109
+ const savedExpectedType = CodeGenState.expectedType;
110
+ CodeGenState.expectedType = typeName;
111
+ const initValue = callbacks.generateExpression(expression);
112
+ CodeGenState.expectedType = savedExpectedType;
119
113
  return initValue;
120
114
  }
121
115
 
122
116
  /**
123
117
  * Check if the last expression was an array initializer
124
118
  */
125
- private _isArrayInitializer(): boolean {
119
+ private static _isArrayInitializer(): boolean {
126
120
  return (
127
- this.deps.arrayInitState.lastArrayInitCount > 0 ||
128
- this.deps.arrayInitState.lastArrayFillValue !== undefined
121
+ CodeGenState.lastArrayInitCount > 0 ||
122
+ CodeGenState.lastArrayFillValue !== undefined
129
123
  );
130
124
  }
131
125
 
132
126
  /**
133
127
  * Process size inference for empty array dimension (u8 data[] <- [1, 2, 3])
134
128
  */
135
- private _processSizeInference(name: string): string {
136
- if (this.deps.arrayInitState.lastArrayFillValue !== undefined) {
129
+ private static _processSizeInference(name: string): string {
130
+ if (CodeGenState.lastArrayFillValue !== undefined) {
137
131
  throw new Error(
138
- `Error: Fill-all syntax [${this.deps.arrayInitState.lastArrayFillValue}*] requires explicit array size`,
132
+ `Error: Fill-all syntax [${CodeGenState.lastArrayFillValue}*] requires explicit array size`,
139
133
  );
140
134
  }
141
135
 
142
136
  // Update type registry with inferred size for .length support
143
- const existingType = this.deps.typeRegistry.get(name);
137
+ const existingType = CodeGenState.typeRegistry.get(name);
144
138
  if (existingType) {
145
- existingType.arrayDimensions = [
146
- this.deps.arrayInitState.lastArrayInitCount,
147
- ];
139
+ existingType.arrayDimensions = [CodeGenState.lastArrayInitCount];
148
140
  }
149
141
 
150
- return `[${this.deps.arrayInitState.lastArrayInitCount}]`;
142
+ return `[${CodeGenState.lastArrayInitCount}]`;
151
143
  }
152
144
 
153
145
  /**
154
146
  * Process explicit array size with validation
155
147
  */
156
- private _processExplicitSize(
148
+ private static _processExplicitSize(
157
149
  arrayDims: Parser.ArrayDimensionContext[],
158
150
  declaredSize: number | null,
151
+ callbacks: IArrayInitCallbacks,
159
152
  ): string {
160
- const dimensionSuffix = this.deps.generateArrayDimensions(arrayDims);
153
+ const dimensionSuffix = callbacks.generateArrayDimensions(arrayDims);
161
154
 
162
155
  // Validate size matches if not using fill-all
163
156
  if (
164
157
  declaredSize !== null &&
165
- this.deps.arrayInitState.lastArrayFillValue === undefined &&
166
- this.deps.arrayInitState.lastArrayInitCount !== declaredSize
158
+ CodeGenState.lastArrayFillValue === undefined &&
159
+ CodeGenState.lastArrayInitCount !== declaredSize
167
160
  ) {
168
161
  throw new Error(
169
- `Error: Array size mismatch - declared [${declaredSize}] but got ${this.deps.arrayInitState.lastArrayInitCount} elements`,
162
+ `Error: Array size mismatch - declared [${declaredSize}] but got ${CodeGenState.lastArrayInitCount} elements`,
170
163
  );
171
164
  }
172
165
 
@@ -176,18 +169,18 @@ class ArrayInitHelper {
176
169
  /**
177
170
  * Expand fill-all syntax (e.g., [0*] with size 5 -> {0, 0, 0, 0, 0})
178
171
  */
179
- private _expandFillAllSyntax(
172
+ private static _expandFillAllSyntax(
180
173
  initValue: string,
181
174
  declaredSize: number | null,
182
175
  ): string {
183
176
  if (
184
- this.deps.arrayInitState.lastArrayFillValue === undefined ||
177
+ CodeGenState.lastArrayFillValue === undefined ||
185
178
  declaredSize === null
186
179
  ) {
187
180
  return initValue;
188
181
  }
189
182
 
190
- const fillVal = this.deps.arrayInitState.lastArrayFillValue;
183
+ const fillVal = CodeGenState.lastArrayFillValue;
191
184
  // C handles {0} correctly, no need to expand
192
185
  if (fillVal === "0") {
193
186
  return initValue;
@@ -5,11 +5,14 @@
5
5
  *
6
6
  * Sets up expectedType and assignmentContext for expression generation,
7
7
  * enabling type-aware resolution of unqualified enum members and overflow behavior.
8
+ *
9
+ * Migrated to use CodeGenState instead of constructor DI.
8
10
  */
9
11
 
10
12
  import * as Parser from "../../../logic/parser/grammar/CNextParser.js";
11
- import TTypeInfo from "../types/TTypeInfo.js";
12
13
  import TOverflowBehavior from "../types/TOverflowBehavior.js";
14
+ import analyzePostfixOps from "../../../../utils/PostfixAnalysisUtils.js";
15
+ import CodeGenState from "../CodeGenState.js";
13
16
 
14
17
  /**
15
18
  * Result of resolving expected type for an assignment target.
@@ -30,58 +33,51 @@ interface IAssignmentContext {
30
33
  overflowBehavior: TOverflowBehavior;
31
34
  }
32
35
 
33
- /**
34
- * Dependencies required for expected type resolution.
35
- */
36
- interface IExpectedTypeResolverDeps {
37
- /** Type registry for looking up variable types */
38
- readonly typeRegistry: ReadonlyMap<string, TTypeInfo>;
39
- /** Struct field types: structName -> (fieldName -> fieldType) */
40
- readonly structFields: ReadonlyMap<string, ReadonlyMap<string, string>>;
41
- /** Check if a type is a known struct */
42
- isKnownStruct: (typeName: string) => boolean;
43
- }
44
-
45
36
  /**
46
37
  * Resolves expected type for assignment targets.
47
38
  */
48
39
  class AssignmentExpectedTypeResolver {
49
- private readonly deps: IExpectedTypeResolverDeps;
50
-
51
- constructor(deps: IExpectedTypeResolverDeps) {
52
- this.deps = deps;
53
- }
54
-
55
40
  /**
56
41
  * Resolve expected type for an assignment target.
57
42
  *
58
43
  * @param targetCtx - The assignment target context
59
44
  * @returns The resolved expected type and assignment context
60
45
  */
61
- resolve(targetCtx: Parser.AssignmentTargetContext): IExpectedTypeResult {
62
- // Case 1: Simple identifier (x <- value)
63
- if (
64
- targetCtx.IDENTIFIER() &&
65
- !targetCtx.memberAccess() &&
66
- !targetCtx.arrayAccess()
67
- ) {
68
- return this.resolveForSimpleIdentifier(targetCtx.IDENTIFIER()!.getText());
46
+ static resolve(
47
+ targetCtx: Parser.AssignmentTargetContext,
48
+ ): IExpectedTypeResult {
49
+ const postfixOps = targetCtx.postfixTargetOp();
50
+ const baseId = targetCtx.IDENTIFIER()?.getText();
51
+
52
+ // Case 1: Simple identifier (x <- value) - no postfix ops
53
+ if (baseId && postfixOps.length === 0) {
54
+ return AssignmentExpectedTypeResolver.resolveForSimpleIdentifier(baseId);
69
55
  }
70
56
 
71
- // Case 2: Member access (config.status <- value)
72
- if (targetCtx.memberAccess()) {
73
- return this.resolveForMemberAccess(targetCtx.memberAccess()!);
57
+ // Case 2: Has member access - extract identifiers from postfix chain
58
+ if (baseId && postfixOps.length > 0) {
59
+ const { identifiers, hasSubscript } = analyzePostfixOps(
60
+ baseId,
61
+ postfixOps,
62
+ );
63
+
64
+ // If we have member access (multiple identifiers), resolve for member chain
65
+ if (identifiers.length >= 2 && !hasSubscript) {
66
+ return AssignmentExpectedTypeResolver.resolveForMemberChain(
67
+ identifiers,
68
+ );
69
+ }
74
70
  }
75
71
 
76
- // Case 3: Array access - no expected type resolution needed
72
+ // Case 3: Array access or complex patterns - no expected type resolution
77
73
  return { expectedType: null, assignmentContext: null };
78
74
  }
79
75
 
80
76
  /**
81
77
  * Resolve expected type for a simple identifier target.
82
78
  */
83
- private resolveForSimpleIdentifier(id: string): IExpectedTypeResult {
84
- const typeInfo = this.deps.typeRegistry.get(id);
79
+ private static resolveForSimpleIdentifier(id: string): IExpectedTypeResult {
80
+ const typeInfo = CodeGenState.typeRegistry.get(id);
85
81
  if (!typeInfo) {
86
82
  return { expectedType: null, assignmentContext: null };
87
83
  }
@@ -97,24 +93,23 @@ class AssignmentExpectedTypeResolver {
97
93
  }
98
94
 
99
95
  /**
100
- * Resolve expected type for a member access target.
96
+ * Resolve expected type for a member access chain.
101
97
  * Walks the chain of struct types to find the final field's type.
102
98
  *
103
99
  * Issue #452: Enables type-aware resolution of unqualified enum members
104
100
  * for nested access (e.g., config.nested.field).
105
101
  */
106
- private resolveForMemberAccess(
107
- memberAccessCtx: Parser.MemberAccessContext,
102
+ private static resolveForMemberChain(
103
+ identifiers: string[],
108
104
  ): IExpectedTypeResult {
109
- const identifiers = memberAccessCtx.IDENTIFIER();
110
105
  if (identifiers.length < 2) {
111
106
  return { expectedType: null, assignmentContext: null };
112
107
  }
113
108
 
114
- const rootName = identifiers[0].getText();
115
- const rootTypeInfo = this.deps.typeRegistry.get(rootName);
109
+ const rootName = identifiers[0];
110
+ const rootTypeInfo = CodeGenState.typeRegistry.get(rootName);
116
111
 
117
- if (!rootTypeInfo || !this.deps.isKnownStruct(rootTypeInfo.baseType)) {
112
+ if (!rootTypeInfo || !CodeGenState.isKnownStruct(rootTypeInfo.baseType)) {
118
113
  return { expectedType: null, assignmentContext: null };
119
114
  }
120
115
 
@@ -122,19 +117,20 @@ class AssignmentExpectedTypeResolver {
122
117
 
123
118
  // Walk through each member in the chain to find the final field's type
124
119
  for (let i = 1; i < identifiers.length && currentStructType; i++) {
125
- const memberName = identifiers[i].getText();
126
- const structFieldTypes = this.deps.structFields.get(currentStructType);
120
+ const memberName = identifiers[i];
121
+ const structFieldTypes: ReadonlyMap<string, string> | undefined =
122
+ CodeGenState.symbols?.structFields.get(currentStructType);
127
123
 
128
124
  if (!structFieldTypes?.has(memberName)) {
129
125
  break;
130
126
  }
131
127
 
132
- const memberType = structFieldTypes.get(memberName)!;
128
+ const memberType: string = structFieldTypes.get(memberName)!;
133
129
 
134
130
  if (i === identifiers.length - 1) {
135
131
  // Last field in chain - this is the assignment target's type
136
132
  return { expectedType: memberType, assignmentContext: null };
137
- } else if (this.deps.isKnownStruct(memberType)) {
133
+ } else if (CodeGenState.isKnownStruct(memberType)) {
138
134
  // Intermediate field - continue walking if it's a struct
139
135
  currentStructType = memberType;
140
136
  } else {
@@ -16,6 +16,7 @@ interface IAssignmentTargetResult {
16
16
 
17
17
  /**
18
18
  * Extracts base identifier from various assignment target patterns.
19
+ * With unified grammar, all patterns use IDENTIFIER postfixTargetOp*.
19
20
  */
20
21
  class AssignmentTargetExtractor {
21
22
  /**
@@ -33,53 +34,24 @@ class AssignmentTargetExtractor {
33
34
  return { baseIdentifier: null, hasSingleIndexSubscript: false };
34
35
  }
35
36
 
36
- // Simple identifier: x <- value
37
- if (target.IDENTIFIER()) {
38
- return {
39
- baseIdentifier: target.IDENTIFIER()!.getText(),
40
- hasSingleIndexSubscript: false,
41
- };
37
+ // All patterns have base IDENTIFIER
38
+ const baseIdentifier = target.IDENTIFIER()?.getText() ?? null;
39
+
40
+ // Check postfixTargetOp for subscripts
41
+ const postfixOps = target.postfixTargetOp();
42
+ let hasSingleIndexSubscript = false;
43
+
44
+ for (const op of postfixOps) {
45
+ const exprs = op.expression();
46
+ if (exprs.length === 1) {
47
+ // Single-index subscript (array access)
48
+ hasSingleIndexSubscript = true;
49
+ break;
50
+ }
51
+ // Two-index subscript (bit extraction) doesn't set the flag
42
52
  }
43
53
 
44
- // Member access: x.field <- value
45
- if (target.memberAccess()) {
46
- return this.extractFromMemberAccess(target.memberAccess()!);
47
- }
48
-
49
- // Array access: x[i] <- value
50
- if (target.arrayAccess()) {
51
- return this.extractFromArrayAccess(target.arrayAccess()!);
52
- }
53
-
54
- return { baseIdentifier: null, hasSingleIndexSubscript: false };
55
- }
56
-
57
- /**
58
- * Extract from member access pattern.
59
- */
60
- private static extractFromMemberAccess(
61
- memberAccess: Parser.MemberAccessContext,
62
- ): IAssignmentTargetResult {
63
- const identifiers = memberAccess.IDENTIFIER();
64
- return {
65
- baseIdentifier: identifiers.length > 0 ? identifiers[0].getText() : null,
66
- hasSingleIndexSubscript: false,
67
- };
68
- }
69
-
70
- /**
71
- * Extract from array access pattern.
72
- * Single-index subscript (x[i]) is array access.
73
- * Two-index subscript (x[0, 8]) is bit extraction.
74
- */
75
- private static extractFromArrayAccess(
76
- arrayAccess: Parser.ArrayAccessContext,
77
- ): IAssignmentTargetResult {
78
- const isSingleIndex = arrayAccess.expression().length === 1;
79
- return {
80
- baseIdentifier: arrayAccess.IDENTIFIER()?.getText() ?? null,
81
- hasSingleIndexSubscript: isSingleIndex,
82
- };
54
+ return { baseIdentifier, hasSingleIndexSubscript };
83
55
  }
84
56
  }
85
57