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.
- package/README.md +86 -63
- package/grammar/CNext.g4 +3 -17
- package/package.json +1 -1
- package/src/cli/serve/ServeCommand.ts +57 -45
- package/src/lib/__tests__/parseCHeader.mocked.test.ts +145 -0
- package/src/transpiler/Transpiler.ts +603 -613
- package/src/transpiler/__tests__/DualCodePaths.test.ts +5 -1
- package/src/transpiler/__tests__/Transpiler.coverage.test.ts +2 -99
- package/src/transpiler/__tests__/Transpiler.test.ts +3 -26
- package/src/transpiler/data/IncludeTreeWalker.ts +1 -1
- package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +23 -52
- package/src/transpiler/logic/parser/grammar/CNext.interp +1 -3
- package/src/transpiler/logic/parser/grammar/CNextListener.ts +0 -22
- package/src/transpiler/logic/parser/grammar/CNextParser.ts +665 -1084
- package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +0 -14
- package/src/transpiler/logic/symbols/CppSymbolCollector.ts +67 -43
- 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 +1410 -2587
- 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 +2082 -52
- 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 +227 -66
- package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +55 -58
- package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +288 -275
- package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +101 -144
- package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +195 -133
- package/src/transpiler/output/codegen/assignment/AssignmentContextBuilder.ts +24 -74
- package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +3 -0
- package/src/transpiler/output/codegen/assignment/IAssignmentContext.ts +3 -0
- package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +290 -320
- package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +42 -0
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +76 -2
- package/src/transpiler/output/codegen/generators/GeneratorRegistry.ts +12 -0
- package/src/transpiler/output/codegen/generators/IOrchestrator.ts +5 -1
- 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/RegisterGenerator.ts +11 -24
- package/src/transpiler/output/codegen/generators/declarationGenerators/RegisterMacroGenerator.ts +64 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +137 -61
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopedRegisterGenerator.ts +18 -27
- 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/expressions/PostfixExpressionGenerator.ts +5 -1
- package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +1 -17
- package/src/transpiler/output/codegen/generators/support/HelperGenerator.ts +23 -22
- package/src/transpiler/output/codegen/helpers/ArrayAccessHelper.ts +129 -0
- package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +54 -61
- package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +40 -44
- package/src/transpiler/output/codegen/helpers/AssignmentTargetExtractor.ts +17 -45
- package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +83 -78
- 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/MemberSeparatorResolver.ts +10 -3
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +103 -96
- package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +44 -0
- package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +9 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ArrayAccessHelper.test.ts +479 -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__/MemberSeparatorResolver.test.ts +1 -0
- package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +191 -453
- package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +201 -0
- package/src/transpiler/output/codegen/helpers/__tests__/TypeGenerationHelper.test.ts +50 -0
- 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/IArrayAccessDeps.ts +23 -0
- package/src/transpiler/output/codegen/types/IArrayAccessInfo.ts +26 -0
- package/src/transpiler/output/codegen/types/IMemberSeparatorDeps.ts +7 -0
- package/src/transpiler/output/codegen/utils/CodegenParserUtils.ts +98 -0
- package/src/transpiler/output/codegen/utils/ExpressionUnwrapper.ts +22 -22
- package/src/transpiler/output/codegen/utils/__tests__/CodegenParserUtils.test.ts +228 -0
- package/src/transpiler/types/IFileResult.ts +0 -4
- package/src/transpiler/types/IPipelineFile.ts +27 -0
- package/src/transpiler/types/IPipelineInput.ts +23 -0
- package/src/transpiler/types/TranspilerState.ts +1 -1
- package/src/utils/FormatUtils.ts +28 -2
- package/src/utils/MapUtils.ts +25 -0
- package/src/utils/PostfixAnalysisUtils.ts +48 -0
- package/src/utils/__tests__/FormatUtils.test.ts +42 -0
- package/src/utils/__tests__/MapUtils.test.ts +85 -0
- package/src/utils/constants/OperatorMappings.ts +19 -0
- package/src/transpiler/logic/StandaloneContextBuilder.ts +0 -150
- package/src/transpiler/logic/__tests__/StandaloneContextBuilder.test.ts +0 -647
- package/src/transpiler/output/codegen/types/ITypeResolverDeps.ts +0 -23
- package/src/transpiler/output/codegen/types/ITypeValidatorDeps.ts +0 -53
- package/src/transpiler/types/ITranspileContext.ts +0 -49
- package/src/transpiler/types/ITranspileContribution.ts +0 -32
|
@@ -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,87 +44,114 @@ 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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
targetCtx.IDENTIFIER()!.getText(),
|
|
56
|
+
const postfixOps = targetCtx.postfixTargetOp();
|
|
57
|
+
const baseId = targetCtx.IDENTIFIER()?.getText();
|
|
58
|
+
|
|
59
|
+
// Case 1: Simple identifier assignment (no postfix ops)
|
|
60
|
+
if (baseId && postfixOps.length === 0) {
|
|
61
|
+
AssignmentValidator.validateSimpleIdentifier(
|
|
62
|
+
baseId,
|
|
80
63
|
expression,
|
|
81
64
|
isCompound,
|
|
65
|
+
callbacks,
|
|
82
66
|
);
|
|
83
67
|
return;
|
|
84
68
|
}
|
|
85
69
|
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
70
|
+
// Analyze postfix ops for member/array patterns
|
|
71
|
+
const identifiers: string[] = baseId ? [baseId] : [];
|
|
72
|
+
const subscriptExprs: Parser.ExpressionContext[] = [];
|
|
73
|
+
|
|
74
|
+
for (const op of postfixOps) {
|
|
75
|
+
if (op.IDENTIFIER()) {
|
|
76
|
+
identifiers.push(op.IDENTIFIER()!.getText());
|
|
77
|
+
} else {
|
|
78
|
+
for (const expr of op.expression()) {
|
|
79
|
+
subscriptExprs.push(expr);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
90
82
|
}
|
|
91
83
|
|
|
92
|
-
// Case
|
|
93
|
-
if (
|
|
94
|
-
|
|
84
|
+
// Case 2: Has subscripts - validate array bounds
|
|
85
|
+
if (subscriptExprs.length > 0 && identifiers.length > 0) {
|
|
86
|
+
AssignmentValidator.validateArrayElement(
|
|
87
|
+
identifiers[0],
|
|
88
|
+
subscriptExprs,
|
|
89
|
+
line,
|
|
90
|
+
callbacks,
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Case 3: Has member access - validate member access
|
|
95
|
+
if (identifiers.length >= 2) {
|
|
96
|
+
AssignmentValidator.validateMemberAccess(
|
|
97
|
+
identifiers,
|
|
98
|
+
expression,
|
|
99
|
+
callbacks,
|
|
100
|
+
);
|
|
95
101
|
}
|
|
96
102
|
}
|
|
97
103
|
|
|
98
104
|
/**
|
|
99
105
|
* Validate simple identifier assignment.
|
|
100
106
|
*/
|
|
101
|
-
private validateSimpleIdentifier(
|
|
107
|
+
private static validateSimpleIdentifier(
|
|
102
108
|
id: string,
|
|
103
109
|
expression: Parser.ExpressionContext,
|
|
104
110
|
isCompound: boolean,
|
|
111
|
+
callbacks: IAssignmentValidatorCallbacks,
|
|
105
112
|
): void {
|
|
106
113
|
// ADR-013: Validate const assignment
|
|
107
|
-
const constError =
|
|
114
|
+
const constError = TypeValidator.checkConstAssignment(id);
|
|
108
115
|
if (constError) {
|
|
109
116
|
throw new Error(constError);
|
|
110
117
|
}
|
|
111
118
|
|
|
112
119
|
// Invalidate float shadow when variable is assigned directly
|
|
113
120
|
const shadowName = `__bits_${id}`;
|
|
114
|
-
|
|
121
|
+
CodeGenState.floatShadowCurrent.delete(shadowName);
|
|
115
122
|
|
|
116
|
-
const targetTypeInfo =
|
|
123
|
+
const targetTypeInfo = CodeGenState.typeRegistry.get(id);
|
|
117
124
|
if (!targetTypeInfo) {
|
|
118
125
|
return;
|
|
119
126
|
}
|
|
120
127
|
|
|
121
|
-
// ADR-017: Validate enum
|
|
128
|
+
// ADR-017: Validate enum assignment for enum-typed variable
|
|
122
129
|
if (targetTypeInfo.isEnum && targetTypeInfo.enumTypeName) {
|
|
123
|
-
|
|
130
|
+
EnumAssignmentValidator.validateEnumAssignment(
|
|
124
131
|
targetTypeInfo.enumTypeName,
|
|
125
132
|
expression,
|
|
126
133
|
);
|
|
127
134
|
}
|
|
128
135
|
|
|
129
136
|
// ADR-024: Validate integer type conversions
|
|
130
|
-
if (
|
|
137
|
+
if (TypeCheckUtils.isInteger(targetTypeInfo.baseType)) {
|
|
131
138
|
try {
|
|
132
|
-
|
|
139
|
+
TypeValidator.validateIntegerAssignment(
|
|
133
140
|
targetTypeInfo.baseType,
|
|
134
141
|
expression.getText(),
|
|
135
|
-
|
|
142
|
+
callbacks.getExpressionType(expression),
|
|
136
143
|
isCompound,
|
|
137
144
|
);
|
|
138
145
|
} catch (validationError) {
|
|
139
|
-
const
|
|
146
|
+
const errorLine = expression.start?.line ?? 0;
|
|
140
147
|
const col = expression.start?.column ?? 0;
|
|
141
148
|
const msg =
|
|
142
149
|
validationError instanceof Error
|
|
143
150
|
? validationError.message
|
|
144
151
|
: String(validationError);
|
|
145
|
-
throw new Error(`${
|
|
152
|
+
throw new Error(`${errorLine}:${col} ${msg}`, {
|
|
153
|
+
cause: validationError,
|
|
154
|
+
});
|
|
146
155
|
}
|
|
147
156
|
}
|
|
148
157
|
}
|
|
@@ -150,27 +159,27 @@ class AssignmentValidator {
|
|
|
150
159
|
/**
|
|
151
160
|
* Validate array element assignment.
|
|
152
161
|
*/
|
|
153
|
-
private validateArrayElement(
|
|
154
|
-
|
|
162
|
+
private static validateArrayElement(
|
|
163
|
+
arrayName: string,
|
|
164
|
+
subscriptExprs: Parser.ExpressionContext[],
|
|
155
165
|
line: number,
|
|
166
|
+
callbacks: IAssignmentValidatorCallbacks,
|
|
156
167
|
): void {
|
|
157
|
-
const arrayName = arrayAccessCtx.IDENTIFIER().getText();
|
|
158
|
-
|
|
159
168
|
// ADR-013: Validate const assignment on array
|
|
160
|
-
const constError =
|
|
169
|
+
const constError = TypeValidator.checkConstAssignment(arrayName);
|
|
161
170
|
if (constError) {
|
|
162
171
|
throw new Error(`${constError} (array element)`);
|
|
163
172
|
}
|
|
164
173
|
|
|
165
174
|
// ADR-036: Compile-time bounds checking
|
|
166
|
-
const typeInfo =
|
|
175
|
+
const typeInfo = CodeGenState.typeRegistry.get(arrayName);
|
|
167
176
|
if (typeInfo?.isArray && typeInfo.arrayDimensions) {
|
|
168
|
-
|
|
177
|
+
TypeValidator.checkArrayBounds(
|
|
169
178
|
arrayName,
|
|
170
179
|
typeInfo.arrayDimensions,
|
|
171
|
-
|
|
180
|
+
subscriptExprs,
|
|
172
181
|
line,
|
|
173
|
-
|
|
182
|
+
callbacks.tryEvaluateConstant,
|
|
174
183
|
);
|
|
175
184
|
}
|
|
176
185
|
}
|
|
@@ -178,32 +187,28 @@ class AssignmentValidator {
|
|
|
178
187
|
/**
|
|
179
188
|
* Validate member access assignment.
|
|
180
189
|
*/
|
|
181
|
-
private validateMemberAccess(
|
|
182
|
-
|
|
190
|
+
private static validateMemberAccess(
|
|
191
|
+
identifiers: string[],
|
|
183
192
|
expression: Parser.ExpressionContext,
|
|
193
|
+
callbacks: IAssignmentValidatorCallbacks,
|
|
184
194
|
): void {
|
|
185
|
-
|
|
186
|
-
if (identifiers.length === 0) {
|
|
195
|
+
if (identifiers.length < 2) {
|
|
187
196
|
return;
|
|
188
197
|
}
|
|
189
198
|
|
|
190
|
-
const rootName = identifiers[0]
|
|
199
|
+
const rootName = identifiers[0];
|
|
200
|
+
const memberName = identifiers[1];
|
|
191
201
|
|
|
192
202
|
// ADR-013: Validate const assignment on struct root
|
|
193
|
-
const constError =
|
|
203
|
+
const constError = TypeValidator.checkConstAssignment(rootName);
|
|
194
204
|
if (constError) {
|
|
195
205
|
throw new Error(`${constError} (member access)`);
|
|
196
206
|
}
|
|
197
207
|
|
|
198
|
-
if (identifiers.length < 2) {
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const memberName = identifiers[1].getText();
|
|
203
208
|
const fullName = `${rootName}_${memberName}`;
|
|
204
209
|
|
|
205
210
|
// ADR-013: Check for read-only register members
|
|
206
|
-
const accessMod =
|
|
211
|
+
const accessMod = CodeGenState.symbols?.registerMemberAccess.get(fullName);
|
|
207
212
|
if (accessMod === "ro") {
|
|
208
213
|
throw new Error(
|
|
209
214
|
`cannot assign to read-only register member '${memberName}' ` +
|
|
@@ -212,19 +217,19 @@ class AssignmentValidator {
|
|
|
212
217
|
}
|
|
213
218
|
|
|
214
219
|
// ADR-029: Validate callback field assignments with nominal typing
|
|
215
|
-
const rootTypeInfo =
|
|
216
|
-
if (rootTypeInfo &&
|
|
220
|
+
const rootTypeInfo = CodeGenState.typeRegistry.get(rootName);
|
|
221
|
+
if (rootTypeInfo && CodeGenState.isKnownStruct(rootTypeInfo.baseType)) {
|
|
217
222
|
const structType = rootTypeInfo.baseType;
|
|
218
223
|
const callbackFieldKey = `${structType}.${memberName}`;
|
|
219
224
|
const expectedCallbackType =
|
|
220
|
-
|
|
225
|
+
CodeGenState.callbackFieldTypes.get(callbackFieldKey);
|
|
221
226
|
|
|
222
227
|
if (expectedCallbackType) {
|
|
223
|
-
|
|
228
|
+
TypeValidator.validateCallbackAssignment(
|
|
224
229
|
expectedCallbackType,
|
|
225
230
|
expression,
|
|
226
231
|
memberName,
|
|
227
|
-
|
|
232
|
+
callbacks.isCallbackTypeUsedAsFieldType,
|
|
228
233
|
);
|
|
229
234
|
}
|
|
230
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;
|