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
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EnumTypeResolver - Handles enum type inference from expressions
|
|
3
|
+
*
|
|
4
|
+
* Extracted from CodeGenerator to reduce complexity.
|
|
5
|
+
* Uses CodeGenState for all state access.
|
|
6
|
+
*
|
|
7
|
+
* ADR-017: Extract enum type from expressions for type-safe comparisons.
|
|
8
|
+
* Handles patterns:
|
|
9
|
+
* - Variable of enum type: `currentState` -> 'State'
|
|
10
|
+
* - Enum member access: `State.IDLE` -> 'State'
|
|
11
|
+
* - Scoped enum member: `Motor.State.IDLE` -> 'Motor_State'
|
|
12
|
+
* - ADR-016: this.State.IDLE -> 'CurrentScope_State'
|
|
13
|
+
* - ADR-016: this.variable -> enum type if variable is of enum type
|
|
14
|
+
* - Function calls returning enum types
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import * as Parser from "../../../logic/parser/grammar/CNextParser";
|
|
18
|
+
import CodeGenState from "../CodeGenState";
|
|
19
|
+
import TypeResolver from "../TypeResolver";
|
|
20
|
+
import ExpressionUnwrapper from "../utils/ExpressionUnwrapper";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Resolves enum types from expressions.
|
|
24
|
+
* All methods are static - uses CodeGenState for state access.
|
|
25
|
+
*/
|
|
26
|
+
export default class EnumTypeResolver {
|
|
27
|
+
/**
|
|
28
|
+
* Extract enum type from an expression.
|
|
29
|
+
* Returns the enum type name if the expression is an enum value, null otherwise.
|
|
30
|
+
*/
|
|
31
|
+
static resolve(
|
|
32
|
+
ctx: Parser.ExpressionContext | Parser.RelationalExpressionContext,
|
|
33
|
+
): string | null {
|
|
34
|
+
const text = ctx.getText();
|
|
35
|
+
|
|
36
|
+
// Check if it's a function call returning an enum
|
|
37
|
+
const enumReturnType = this.getFunctionCallEnumType(text);
|
|
38
|
+
if (enumReturnType) {
|
|
39
|
+
return enumReturnType;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check if it's a simple identifier that's an enum variable
|
|
43
|
+
if (/^[a-zA-Z_]\w*$/.exec(text)) {
|
|
44
|
+
const typeInfo = CodeGenState.typeRegistry.get(text);
|
|
45
|
+
if (typeInfo?.isEnum && typeInfo.enumTypeName) {
|
|
46
|
+
return typeInfo.enumTypeName;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Check member access patterns: EnumType.MEMBER, Scope.EnumType.MEMBER, etc.
|
|
51
|
+
const memberResult = this.getEnumTypeFromMemberAccess(text.split("."));
|
|
52
|
+
if (memberResult) {
|
|
53
|
+
return memberResult;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Fallback: use TypeResolver to resolve the full expression type through
|
|
57
|
+
// struct member chains (e.g. global.config.inputs[0].assignedValue -> EValueId)
|
|
58
|
+
return this.resolveViaTypeResolver(ctx);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Fallback resolution via TypeResolver for complex expressions.
|
|
63
|
+
* Handles struct member chains like global.struct.field that resolve to enum types.
|
|
64
|
+
*/
|
|
65
|
+
private static resolveViaTypeResolver(
|
|
66
|
+
ctx: Parser.ExpressionContext | Parser.RelationalExpressionContext,
|
|
67
|
+
): string | null {
|
|
68
|
+
// ExpressionContext has getPostfixExpression, RelationalExpressionContext does not
|
|
69
|
+
if (!("ternaryExpression" in ctx)) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
const postfix = ExpressionUnwrapper.getPostfixExpression(ctx);
|
|
73
|
+
if (!postfix) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const resolvedType = TypeResolver.getPostfixExpressionType(postfix);
|
|
77
|
+
if (resolvedType && CodeGenState.isKnownEnum(resolvedType)) {
|
|
78
|
+
return resolvedType;
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Check if parts represent an enum member access and return the enum type.
|
|
85
|
+
*/
|
|
86
|
+
private static getEnumTypeFromMemberAccess(parts: string[]): string | null {
|
|
87
|
+
if (parts.length < 2) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ADR-016: Check this.State.IDLE pattern
|
|
92
|
+
const thisEnumType = this.getEnumTypeFromThisEnum(parts);
|
|
93
|
+
if (thisEnumType) return thisEnumType;
|
|
94
|
+
|
|
95
|
+
// Issue #478: Check global.Enum.Member pattern
|
|
96
|
+
const globalEnumType = this.getEnumTypeFromGlobalEnum(parts);
|
|
97
|
+
if (globalEnumType) return globalEnumType;
|
|
98
|
+
|
|
99
|
+
// ADR-016: Check this.variable pattern
|
|
100
|
+
const thisVarType = this.getEnumTypeFromThisVariable(parts);
|
|
101
|
+
if (thisVarType) return thisVarType;
|
|
102
|
+
|
|
103
|
+
// Check simple enum: State.IDLE
|
|
104
|
+
const possibleEnum = parts[0];
|
|
105
|
+
if (CodeGenState.isKnownEnum(possibleEnum)) {
|
|
106
|
+
return possibleEnum;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Check scoped enum: Motor.State.IDLE -> Motor_State
|
|
110
|
+
return this.getEnumTypeFromScopedEnum(parts);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* ADR-016: Check this.State.IDLE pattern (this.Enum.Member inside scope)
|
|
115
|
+
*/
|
|
116
|
+
private static getEnumTypeFromThisEnum(parts: string[]): string | null {
|
|
117
|
+
if (parts[0] !== "this" || !CodeGenState.currentScope || parts.length < 3) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
const enumName = parts[1];
|
|
121
|
+
const scopedEnumName = `${CodeGenState.currentScope}_${enumName}`;
|
|
122
|
+
return CodeGenState.isKnownEnum(scopedEnumName) ? scopedEnumName : null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Issue #478: Check global.Enum.Member pattern (global.ECategory.CAT_A)
|
|
127
|
+
*/
|
|
128
|
+
private static getEnumTypeFromGlobalEnum(parts: string[]): string | null {
|
|
129
|
+
if (parts[0] !== "global" || parts.length < 3) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
const enumName = parts[1];
|
|
133
|
+
return CodeGenState.isKnownEnum(enumName) ? enumName : null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* ADR-016: Check this.variable pattern (this.varName where varName is enum type)
|
|
138
|
+
*/
|
|
139
|
+
private static getEnumTypeFromThisVariable(parts: string[]): string | null {
|
|
140
|
+
if (
|
|
141
|
+
parts[0] !== "this" ||
|
|
142
|
+
!CodeGenState.currentScope ||
|
|
143
|
+
parts.length !== 2
|
|
144
|
+
) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
const varName = parts[1];
|
|
148
|
+
const scopedVarName = `${CodeGenState.currentScope}_${varName}`;
|
|
149
|
+
const typeInfo = CodeGenState.typeRegistry.get(scopedVarName);
|
|
150
|
+
if (typeInfo?.isEnum && typeInfo.enumTypeName) {
|
|
151
|
+
return typeInfo.enumTypeName;
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Check scoped enum: Motor.State.IDLE -> Motor_State
|
|
158
|
+
*/
|
|
159
|
+
private static getEnumTypeFromScopedEnum(parts: string[]): string | null {
|
|
160
|
+
if (parts.length < 3) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
const scopeName = parts[0];
|
|
164
|
+
const enumName = parts[1];
|
|
165
|
+
const scopedEnumName = `${scopeName}_${enumName}`;
|
|
166
|
+
return CodeGenState.isKnownEnum(scopedEnumName) ? scopedEnumName : null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Check if an expression is a function call returning an enum type.
|
|
171
|
+
* Handles patterns:
|
|
172
|
+
* - func() or func(args) - global function
|
|
173
|
+
* - Scope.method() or Scope.method(args) - scope method from outside
|
|
174
|
+
* - this.method() or this.method(args) - scope method from inside
|
|
175
|
+
* - global.func() or global.func(args) - global function from inside scope
|
|
176
|
+
* - global.Scope.method() or global.Scope.method(args) - scope method from inside another scope
|
|
177
|
+
*/
|
|
178
|
+
private static getFunctionCallEnumType(text: string): string | null {
|
|
179
|
+
// Check if this looks like a function call (contains parentheses)
|
|
180
|
+
const parenIndex = text.indexOf("(");
|
|
181
|
+
if (parenIndex === -1) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Extract the function reference (everything before the opening paren)
|
|
186
|
+
const funcRef = text.substring(0, parenIndex);
|
|
187
|
+
const parts = funcRef.split(".");
|
|
188
|
+
|
|
189
|
+
let fullFuncName: string | null = null;
|
|
190
|
+
|
|
191
|
+
if (parts.length === 1) {
|
|
192
|
+
// Simple function call: func()
|
|
193
|
+
fullFuncName = parts[0];
|
|
194
|
+
} else if (parts.length === 2) {
|
|
195
|
+
if (parts[0] === "this" && CodeGenState.currentScope) {
|
|
196
|
+
// this.method() -> Scope_method
|
|
197
|
+
fullFuncName = `${CodeGenState.currentScope}_${parts[1]}`;
|
|
198
|
+
} else if (parts[0] === "global") {
|
|
199
|
+
// global.func() -> func
|
|
200
|
+
fullFuncName = parts[1];
|
|
201
|
+
} else if (CodeGenState.isKnownScope(parts[0])) {
|
|
202
|
+
// Scope.method() -> Scope_method
|
|
203
|
+
fullFuncName = `${parts[0]}_${parts[1]}`;
|
|
204
|
+
}
|
|
205
|
+
} else if (parts.length === 3) {
|
|
206
|
+
if (parts[0] === "global" && CodeGenState.isKnownScope(parts[1])) {
|
|
207
|
+
// global.Scope.method() -> Scope_method
|
|
208
|
+
fullFuncName = `${parts[1]}_${parts[2]}`;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (!fullFuncName) {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Look up the function's return type
|
|
217
|
+
const returnType = CodeGenState.getFunctionReturnType(fullFuncName);
|
|
218
|
+
if (!returnType) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Check if the return type is an enum
|
|
223
|
+
if (CodeGenState.isKnownEnum(returnType)) {
|
|
224
|
+
return returnType;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ScopeResolver - Handles scope visibility and access validation
|
|
3
|
+
*
|
|
4
|
+
* Extracted from CodeGenerator to reduce complexity.
|
|
5
|
+
* Uses CodeGenState for all state access.
|
|
6
|
+
*
|
|
7
|
+
* ADR-016: Validates cross-scope member access visibility rules.
|
|
8
|
+
* Issue #165: Enforces that:
|
|
9
|
+
* - Cannot reference own scope by name (must use this. prefix)
|
|
10
|
+
* - Cannot access private members from outside the scope
|
|
11
|
+
* - Exception: global.Scope.member is allowed for explicit qualification
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import CodeGenState from "../CodeGenState";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Resolves scope visibility and validates cross-scope access.
|
|
18
|
+
* All methods are static - uses CodeGenState for state access.
|
|
19
|
+
*/
|
|
20
|
+
export default class ScopeResolver {
|
|
21
|
+
/**
|
|
22
|
+
* Validate cross-scope visibility for member access.
|
|
23
|
+
* Throws an error if the access violates visibility rules.
|
|
24
|
+
*
|
|
25
|
+
* @param scopeName - The scope being accessed
|
|
26
|
+
* @param memberName - The member being accessed
|
|
27
|
+
* @param isGlobalAccess - Whether this is a global.Scope.member access
|
|
28
|
+
*/
|
|
29
|
+
static validateCrossScopeVisibility(
|
|
30
|
+
scopeName: string,
|
|
31
|
+
memberName: string,
|
|
32
|
+
isGlobalAccess: boolean = false,
|
|
33
|
+
): void {
|
|
34
|
+
// Error if referencing own scope by name (must use this. prefix)
|
|
35
|
+
// Exception: global.Scope.member is allowed for explicit qualification
|
|
36
|
+
if (!isGlobalAccess && CodeGenState.currentScope === scopeName) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
`Error: Cannot reference own scope '${scopeName}' by name. ` +
|
|
39
|
+
`Use 'this.${memberName}' instead of '${scopeName}.${memberName}'`,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Check private member access (skip for own scope - we can access our own privates)
|
|
44
|
+
const isOwnScope = CodeGenState.currentScope === scopeName;
|
|
45
|
+
if (!isOwnScope) {
|
|
46
|
+
const visibility = CodeGenState.symbols?.scopeMemberVisibility
|
|
47
|
+
.get(scopeName)
|
|
48
|
+
?.get(memberName);
|
|
49
|
+
if (visibility === "private") {
|
|
50
|
+
const context = CodeGenState.currentScope
|
|
51
|
+
? `from scope '${CodeGenState.currentScope}'`
|
|
52
|
+
: "from outside the scope";
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Cannot access private member '${memberName}' of scope '${scopeName}' ${context}. ` +
|
|
55
|
+
`Only public members are accessible outside their scope.`,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SizeofResolver - Handles sizeof expression generation
|
|
3
|
+
*
|
|
4
|
+
* Extracted from CodeGenerator to reduce complexity.
|
|
5
|
+
* Uses CodeGenState for all state access.
|
|
6
|
+
*
|
|
7
|
+
* ADR-023: sizeof expression handling with safety checks:
|
|
8
|
+
* - E0601: sizeof on array parameter is error (returns pointer size)
|
|
9
|
+
* - E0602: Side effects in sizeof are error (MISRA C:2012 Rule 13.6)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import * as Parser from "../../../logic/parser/grammar/CNextParser";
|
|
13
|
+
import CodeGenState from "../CodeGenState";
|
|
14
|
+
import ExpressionUnwrapper from "../utils/ExpressionUnwrapper";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Callbacks for operations that require CodeGenerator context.
|
|
18
|
+
* These are the minimal dependencies that can't be replaced with CodeGenState.
|
|
19
|
+
*/
|
|
20
|
+
interface ISizeofCallbacks {
|
|
21
|
+
generateType: (ctx: Parser.TypeContext) => string;
|
|
22
|
+
generateExpression: (ctx: Parser.ExpressionContext) => string;
|
|
23
|
+
hasSideEffects: (ctx: Parser.ExpressionContext) => boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Resolves sizeof expressions to C code.
|
|
28
|
+
*/
|
|
29
|
+
export default class SizeofResolver {
|
|
30
|
+
/**
|
|
31
|
+
* Generate sizeof expression.
|
|
32
|
+
* sizeof(type) -> sizeof(c_type)
|
|
33
|
+
* sizeof(variable) -> sizeof(variable)
|
|
34
|
+
*/
|
|
35
|
+
static generate(
|
|
36
|
+
ctx: Parser.SizeofExpressionContext,
|
|
37
|
+
callbacks: ISizeofCallbacks,
|
|
38
|
+
): string {
|
|
39
|
+
// Check if it's sizeof(type) or sizeof(expression)
|
|
40
|
+
// Note: Due to grammar ambiguity, sizeof(variable) may parse as sizeof(type)
|
|
41
|
+
// when the variable name matches userType (just an identifier)
|
|
42
|
+
if (ctx.type()) {
|
|
43
|
+
return this.sizeofType(ctx.type()!, callbacks);
|
|
44
|
+
}
|
|
45
|
+
return this.sizeofExpression(ctx.expression()!, callbacks);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Handle sizeof(type) - may actually be sizeof(variable) due to grammar ambiguity
|
|
50
|
+
*/
|
|
51
|
+
private static sizeofType(
|
|
52
|
+
typeCtx: Parser.TypeContext,
|
|
53
|
+
callbacks: ISizeofCallbacks,
|
|
54
|
+
): string {
|
|
55
|
+
// qualifiedType matches IDENTIFIER.IDENTIFIER, could be struct.member
|
|
56
|
+
if (typeCtx.qualifiedType()) {
|
|
57
|
+
const result = this.sizeofQualifiedType(typeCtx.qualifiedType()!);
|
|
58
|
+
if (result) return result;
|
|
59
|
+
// Fall through to generateType for actual type references (Scope.Type)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// userType is just IDENTIFIER, could be a variable reference
|
|
63
|
+
if (typeCtx.userType()) {
|
|
64
|
+
return this.sizeofUserType(typeCtx.getText());
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// It's a primitive or other type - generate normally
|
|
68
|
+
return `sizeof(${callbacks.generateType(typeCtx)})`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Handle sizeof(qualified.type) - may be struct.member access
|
|
73
|
+
* Returns null if this is actually a type reference (Scope.Type)
|
|
74
|
+
*/
|
|
75
|
+
private static sizeofQualifiedType(
|
|
76
|
+
qualifiedCtx: Parser.QualifiedTypeContext,
|
|
77
|
+
): string | null {
|
|
78
|
+
const identifiers = qualifiedCtx.IDENTIFIER();
|
|
79
|
+
const firstName = identifiers[0].getText();
|
|
80
|
+
const memberName = identifiers[1].getText();
|
|
81
|
+
|
|
82
|
+
// Check if first identifier is a local variable (struct instance)
|
|
83
|
+
if (CodeGenState.localVariables.has(firstName)) {
|
|
84
|
+
return `sizeof(${firstName}.${memberName})`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Check if first identifier is a parameter (struct parameter)
|
|
88
|
+
const paramInfo = CodeGenState.currentParameters.get(firstName);
|
|
89
|
+
if (paramInfo) {
|
|
90
|
+
const sep = paramInfo.isStruct ? "->" : ".";
|
|
91
|
+
return `sizeof(${firstName}${sep}${memberName})`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Check if first identifier is a global variable
|
|
95
|
+
// If not a scope or enum, it's likely a global struct variable
|
|
96
|
+
if (
|
|
97
|
+
!CodeGenState.isKnownScope(firstName) &&
|
|
98
|
+
!CodeGenState.isKnownEnum(firstName)
|
|
99
|
+
) {
|
|
100
|
+
return `sizeof(${firstName}.${memberName})`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// It's an actual type reference (Scope.Type), return null to fall through
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Handle sizeof(identifier) - could be variable or type name
|
|
109
|
+
*/
|
|
110
|
+
private static sizeofUserType(varName: string): string {
|
|
111
|
+
// Check if it's a known parameter
|
|
112
|
+
const paramInfo = CodeGenState.currentParameters.get(varName);
|
|
113
|
+
if (paramInfo) {
|
|
114
|
+
return this.sizeofParameter(varName, paramInfo);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Check if it's a known local variable, struct type, or enum type
|
|
118
|
+
// For all these cases, generate sizeof(name) directly
|
|
119
|
+
// Unknown identifiers are also treated as variables for safety
|
|
120
|
+
return `sizeof(${varName})`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Handle sizeof on a parameter - validates and generates appropriate code
|
|
125
|
+
*/
|
|
126
|
+
private static sizeofParameter(
|
|
127
|
+
varName: string,
|
|
128
|
+
paramInfo: { isArray?: boolean; isCallback?: boolean; isStruct?: boolean },
|
|
129
|
+
): string {
|
|
130
|
+
// E0601: Array parameters decay to pointers
|
|
131
|
+
if (paramInfo.isArray) {
|
|
132
|
+
this.throwArrayParamSizeofError(varName);
|
|
133
|
+
}
|
|
134
|
+
// For pass-by-reference parameters (non-array, non-callback, non-struct),
|
|
135
|
+
// use pointer dereference
|
|
136
|
+
if (!paramInfo.isCallback && !paramInfo.isStruct) {
|
|
137
|
+
return `sizeof(*${varName})`;
|
|
138
|
+
}
|
|
139
|
+
return `sizeof(${varName})`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Throw E0601 error for sizeof on array parameter
|
|
144
|
+
*/
|
|
145
|
+
private static throwArrayParamSizeofError(varName: string): never {
|
|
146
|
+
throw new Error(
|
|
147
|
+
`Error[E0601]: sizeof() on array parameter '${varName}' returns pointer size. ` +
|
|
148
|
+
`Use ${varName}.length for element count or sizeof(elementType) * ${varName}.length for bytes`,
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Handle sizeof(expression) with validation
|
|
154
|
+
*/
|
|
155
|
+
private static sizeofExpression(
|
|
156
|
+
expr: Parser.ExpressionContext,
|
|
157
|
+
callbacks: ISizeofCallbacks,
|
|
158
|
+
): string {
|
|
159
|
+
// E0601: Check if expression is an array parameter
|
|
160
|
+
const varName = ExpressionUnwrapper.getSimpleIdentifier(expr);
|
|
161
|
+
if (varName) {
|
|
162
|
+
const paramInfo = CodeGenState.currentParameters.get(varName);
|
|
163
|
+
if (paramInfo?.isArray) {
|
|
164
|
+
this.throwArrayParamSizeofError(varName);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// E0602: Check for side effects
|
|
169
|
+
if (callbacks.hasSideEffects(expr)) {
|
|
170
|
+
throw new Error(
|
|
171
|
+
`Error[E0602]: sizeof() operand must not have side effects (MISRA C:2012 Rule 13.6)`,
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return `sizeof(${callbacks.generateExpression(expr)})`;
|
|
176
|
+
}
|
|
177
|
+
}
|