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.
- package/README.md +86 -63
- package/package.json +1 -1
- package/src/transpiler/Transpiler.ts +3 -2
- package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
- package/src/transpiler/__tests__/Transpiler.coverage.test.ts +1 -1
- package/src/transpiler/__tests__/Transpiler.test.ts +0 -23
- package/src/transpiler/logic/symbols/cnext/collectors/StructCollector.ts +156 -70
- package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +31 -6
- package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +43 -11
- package/src/transpiler/output/codegen/CodeGenState.ts +811 -0
- package/src/transpiler/output/codegen/CodeGenerator.ts +817 -1377
- package/src/transpiler/output/codegen/TypeResolver.ts +193 -149
- package/src/transpiler/output/codegen/TypeValidator.ts +148 -370
- package/src/transpiler/output/codegen/__tests__/CodeGenState.test.ts +446 -0
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +326 -60
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +1 -1
- package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +435 -196
- package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +51 -67
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +495 -471
- package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +39 -43
- package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +52 -55
- package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +122 -62
- package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +101 -144
- package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +143 -126
- package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +287 -320
- package/src/transpiler/output/codegen/generators/GeneratorRegistry.ts +12 -0
- package/src/transpiler/output/codegen/generators/__tests__/GeneratorRegistry.test.ts +28 -1
- package/src/transpiler/output/codegen/generators/declarationGenerators/ArrayDimensionUtils.ts +67 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +121 -51
- package/src/transpiler/output/codegen/generators/declarationGenerators/StructGenerator.ts +100 -23
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ArrayDimensionUtils.test.ts +125 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +157 -4
- package/src/transpiler/output/codegen/generators/support/HelperGenerator.ts +23 -22
- package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +54 -61
- package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +21 -30
- package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +56 -53
- package/src/transpiler/output/codegen/helpers/CppModeHelper.ts +22 -30
- package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +108 -50
- package/src/transpiler/output/codegen/helpers/FloatBitHelper.ts +16 -31
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +103 -96
- package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +9 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +58 -103
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +97 -40
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +223 -128
- package/src/transpiler/output/codegen/helpers/__tests__/CppModeHelper.test.ts +68 -41
- package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +198 -47
- package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +39 -37
- package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +191 -453
- package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +229 -0
- package/src/transpiler/output/codegen/resolution/ScopeResolver.ts +60 -0
- package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +177 -0
- package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +336 -0
- package/src/transpiler/output/codegen/resolution/__tests__/SizeofResolver.test.ts +201 -0
- package/src/transpiler/output/codegen/types/ITypeResolverDeps.ts +0 -23
- 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
|
|
20
|
+
import CodeGenState from "../CodeGenState.js";
|
|
21
|
+
import TypeCheckUtils from "../../../../utils/TypeCheckUtils.js";
|
|
19
22
|
|
|
20
23
|
/**
|
|
21
|
-
*
|
|
24
|
+
* Callbacks required for assignment validation.
|
|
25
|
+
* These need CodeGenerator context and cannot be replaced with static state.
|
|
22
26
|
*/
|
|
23
|
-
interface
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
121
|
+
CodeGenState.floatShadowCurrent.delete(shadowName);
|
|
123
122
|
|
|
124
|
-
const targetTypeInfo =
|
|
123
|
+
const targetTypeInfo = CodeGenState.typeRegistry.get(id);
|
|
125
124
|
if (!targetTypeInfo) {
|
|
126
125
|
return;
|
|
127
126
|
}
|
|
128
127
|
|
|
129
|
-
// ADR-017: Validate enum
|
|
128
|
+
// ADR-017: Validate enum assignment for enum-typed variable
|
|
130
129
|
if (targetTypeInfo.isEnum && targetTypeInfo.enumTypeName) {
|
|
131
|
-
|
|
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 (
|
|
137
|
+
if (TypeCheckUtils.isInteger(targetTypeInfo.baseType)) {
|
|
139
138
|
try {
|
|
140
|
-
|
|
139
|
+
TypeValidator.validateIntegerAssignment(
|
|
141
140
|
targetTypeInfo.baseType,
|
|
142
141
|
expression.getText(),
|
|
143
|
-
|
|
142
|
+
callbacks.getExpressionType(expression),
|
|
144
143
|
isCompound,
|
|
145
144
|
);
|
|
146
145
|
} catch (validationError) {
|
|
147
|
-
const
|
|
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(`${
|
|
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 =
|
|
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 =
|
|
175
|
+
const typeInfo = CodeGenState.typeRegistry.get(arrayName);
|
|
174
176
|
if (typeInfo?.isArray && typeInfo.arrayDimensions) {
|
|
175
|
-
|
|
177
|
+
TypeValidator.checkArrayBounds(
|
|
176
178
|
arrayName,
|
|
177
179
|
typeInfo.arrayDimensions,
|
|
178
180
|
subscriptExprs,
|
|
179
181
|
line,
|
|
180
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
218
|
-
if (rootTypeInfo &&
|
|
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
|
-
|
|
225
|
+
CodeGenState.callbackFieldTypes.get(callbackFieldKey);
|
|
223
226
|
|
|
224
227
|
if (expectedCallbackType) {
|
|
225
|
-
|
|
228
|
+
TypeValidator.validateCallbackAssignment(
|
|
226
229
|
expectedCallbackType,
|
|
227
230
|
expression,
|
|
228
231
|
memberName,
|
|
229
|
-
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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 (!
|
|
36
|
+
if (!CodeGenState.isKnownEnum(typeName)) {
|
|
52
37
|
return;
|
|
53
38
|
}
|
|
54
39
|
|
|
55
|
-
const valueEnumType =
|
|
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 (
|
|
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
|
-
|
|
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(
|
|
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" &&
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
99
|
-
|
|
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 -
|
|
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]
|
|
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;
|