c-next 0.2.6 → 0.2.7
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/dist/index.js +387 -433
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
- package/src/transpiler/Transpiler.ts +50 -29
- package/src/transpiler/__tests__/Transpiler.coverage.test.ts +2 -1
- package/src/transpiler/data/PathResolver.ts +10 -6
- package/src/transpiler/data/__tests__/PathResolver.test.ts +32 -0
- package/src/transpiler/logic/analysis/PassByValueAnalyzer.ts +1 -12
- package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +239 -0
- package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +414 -0
- package/src/transpiler/logic/analysis/__tests__/StructFieldAnalyzer.test.ts +15 -75
- package/src/transpiler/logic/analysis/__tests__/runAnalyzers.test.ts +3 -15
- package/src/transpiler/logic/analysis/runAnalyzers.ts +11 -2
- package/src/transpiler/logic/analysis/types/ISignedShiftError.ts +15 -0
- package/src/transpiler/logic/symbols/SymbolUtils.ts +4 -1
- package/src/transpiler/logic/symbols/__tests__/SymbolUtils.test.ts +10 -11
- package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +4 -4
- package/src/transpiler/logic/symbols/cpp/__tests__/CppResolver.integration.test.ts +4 -4
- package/src/transpiler/output/codegen/CodeGenerator.ts +12 -4
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +3 -3
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +26 -26
- package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +12 -11
- package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +21 -21
- package/src/transpiler/output/codegen/generators/expressions/AccessExprGenerator.ts +7 -326
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +14 -275
- package/src/transpiler/output/codegen/generators/expressions/UnaryExprGenerator.ts +13 -1
- package/src/transpiler/output/codegen/generators/expressions/__tests__/AccessExprGenerator.test.ts +0 -573
- package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +8 -439
- package/src/transpiler/output/codegen/generators/expressions/__tests__/UnaryExprGenerator.test.ts +196 -0
- package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +7 -2
- package/src/transpiler/output/codegen/helpers/TypedefParamParser.ts +34 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +48 -0
- package/src/transpiler/output/codegen/helpers/__tests__/TypedefParamParser.test.ts +88 -0
- package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +15 -2
- package/src/transpiler/output/headers/__tests__/BaseHeaderGenerator.test.ts +87 -0
- package/src/utils/ExpressionUtils.ts +51 -0
- package/src/utils/types/IParameterSymbol.ts +2 -0
|
@@ -1,337 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Access Expression Generator (ADR-053 A2 Phase 6)
|
|
3
3
|
*
|
|
4
|
-
* Generates C code for property access
|
|
5
|
-
* - .
|
|
6
|
-
* - .capacity
|
|
7
|
-
* - .size property for strings (buffer size = capacity + 1)
|
|
4
|
+
* Generates C code for string buffer property access:
|
|
5
|
+
* - .capacity → compile-time max string length (excluding null terminator)
|
|
6
|
+
* - .size → compile-time buffer size (capacity + 1, for null terminator)
|
|
8
7
|
*
|
|
9
8
|
* Also provides helper for bitmap field access.
|
|
9
|
+
*
|
|
10
|
+
* Note: Explicit length properties (.bit_length, .byte_length, .element_count,
|
|
11
|
+
* .char_count) are handled in PostfixExpressionGenerator.ts, not here.
|
|
12
|
+
* The deprecated .length property was removed per ADR-058.
|
|
10
13
|
*/
|
|
11
14
|
import IGeneratorOutput from "../IGeneratorOutput";
|
|
12
|
-
import TGeneratorEffect from "../TGeneratorEffect";
|
|
13
15
|
import TTypeInfo from "../../types/TTypeInfo";
|
|
14
16
|
|
|
15
|
-
// Type width mappings - C-Next integer types to their bit widths
|
|
16
|
-
const TYPE_WIDTH: Record<string, number> = {
|
|
17
|
-
u8: 8,
|
|
18
|
-
u16: 16,
|
|
19
|
-
u32: 32,
|
|
20
|
-
u64: 64,
|
|
21
|
-
i8: 8,
|
|
22
|
-
i16: 16,
|
|
23
|
-
i32: 32,
|
|
24
|
-
i64: 64,
|
|
25
|
-
f32: 32,
|
|
26
|
-
f64: 64,
|
|
27
|
-
bool: 1,
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
// C type widths for header-defined types
|
|
31
|
-
const C_TYPE_WIDTH: Record<string, number> = {
|
|
32
|
-
uint8_t: 8,
|
|
33
|
-
uint16_t: 16,
|
|
34
|
-
uint32_t: 32,
|
|
35
|
-
uint64_t: 64,
|
|
36
|
-
int8_t: 8,
|
|
37
|
-
int16_t: 16,
|
|
38
|
-
int32_t: 32,
|
|
39
|
-
int64_t: 64,
|
|
40
|
-
float: 32,
|
|
41
|
-
double: 64,
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Context passed to property generators.
|
|
46
|
-
* Contains the current expression state needed for property resolution.
|
|
47
|
-
*/
|
|
48
|
-
interface PropertyContext {
|
|
49
|
-
/** Current result string being built */
|
|
50
|
-
result: string;
|
|
51
|
-
/** Primary identifier (if any) */
|
|
52
|
-
primaryId: string | undefined;
|
|
53
|
-
/** Current resolved identifier (for type lookups) */
|
|
54
|
-
currentIdentifier: string | undefined;
|
|
55
|
-
/** How many array dimensions have been subscripted */
|
|
56
|
-
subscriptDepth: number;
|
|
57
|
-
/** Previous struct type in chain (for struct member .length) */
|
|
58
|
-
previousStructType: string | undefined;
|
|
59
|
-
/** Previous member name in chain (for struct member .length) */
|
|
60
|
-
previousMemberName: string | undefined;
|
|
61
|
-
/** Type info from type registry */
|
|
62
|
-
typeInfo: TTypeInfo | undefined;
|
|
63
|
-
/** Main function args parameter name (for args.length -> argc) */
|
|
64
|
-
mainArgsName: string | undefined;
|
|
65
|
-
/** Length cache for string lengths */
|
|
66
|
-
lengthCache: ReadonlyMap<string, string> | undefined;
|
|
67
|
-
/** Struct field info lookup function */
|
|
68
|
-
getStructFieldInfo: (
|
|
69
|
-
structType: string,
|
|
70
|
-
fieldName: string,
|
|
71
|
-
) => { type: string; dimensions?: number[] } | undefined;
|
|
72
|
-
/** Issue #201: Bitmap bit width lookup function */
|
|
73
|
-
getBitmapBitWidth?: (bitmapTypeName: string) => number | undefined;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Result of property generation.
|
|
78
|
-
*/
|
|
79
|
-
interface PropertyResult {
|
|
80
|
-
/** Generated code (or null if property wasn't handled) */
|
|
81
|
-
code: string | null;
|
|
82
|
-
/** Effects to apply */
|
|
83
|
-
effects: TGeneratorEffect[];
|
|
84
|
-
/** Whether to skip further processing in the loop */
|
|
85
|
-
skipContinue: boolean;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Get bit width for a type, checking both C-Next and C types.
|
|
90
|
-
*/
|
|
91
|
-
function getTypeBitWidth(typeName: string): number {
|
|
92
|
-
return TYPE_WIDTH[typeName] || C_TYPE_WIDTH[typeName] || 0;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Create a PropertyResult with strlen effect.
|
|
97
|
-
*/
|
|
98
|
-
function makeStrlenResult(expr: string, skipContinue: boolean): PropertyResult {
|
|
99
|
-
return {
|
|
100
|
-
code: `strlen(${expr})`,
|
|
101
|
-
effects: [{ type: "include", header: "string" }],
|
|
102
|
-
skipContinue,
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Create a PropertyResult for bit width (or unsupported type comment).
|
|
108
|
-
* @param isElement - true for array element types (uses "element type" in error)
|
|
109
|
-
*/
|
|
110
|
-
function makeBitWidthResult(
|
|
111
|
-
typeName: string,
|
|
112
|
-
skipContinue: boolean,
|
|
113
|
-
isElement: boolean = false,
|
|
114
|
-
): PropertyResult {
|
|
115
|
-
const bitWidth = getTypeBitWidth(typeName);
|
|
116
|
-
if (bitWidth > 0) {
|
|
117
|
-
return { code: String(bitWidth), effects: [], skipContinue };
|
|
118
|
-
}
|
|
119
|
-
const typeLabel = isElement ? "element type" : "type";
|
|
120
|
-
return {
|
|
121
|
-
code: `/* .length: unsupported ${typeLabel} ${typeName} */0`,
|
|
122
|
-
effects: [],
|
|
123
|
-
skipContinue,
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Handle .length for struct member access (cfg.field.length).
|
|
129
|
-
*/
|
|
130
|
-
function handleStructMemberLength(ctx: PropertyContext): PropertyResult | null {
|
|
131
|
-
if (!ctx.previousStructType || !ctx.previousMemberName) {
|
|
132
|
-
return null;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const fieldInfo = ctx.getStructFieldInfo(
|
|
136
|
-
ctx.previousStructType,
|
|
137
|
-
ctx.previousMemberName,
|
|
138
|
-
);
|
|
139
|
-
if (!fieldInfo) {
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const { type: memberType, dimensions } = fieldInfo;
|
|
144
|
-
const isStringField = memberType.startsWith("string<");
|
|
145
|
-
|
|
146
|
-
// String array field: string<64> arr[4]
|
|
147
|
-
if (dimensions && dimensions.length > 1 && isStringField) {
|
|
148
|
-
if (ctx.subscriptDepth === 0) {
|
|
149
|
-
return { code: String(dimensions[0]), effects: [], skipContinue: true };
|
|
150
|
-
}
|
|
151
|
-
return makeStrlenResult(ctx.result, true);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Single string field: string<64> str
|
|
155
|
-
if (dimensions?.length === 1 && isStringField) {
|
|
156
|
-
return makeStrlenResult(ctx.result, true);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Multi-dim array member with partial subscript
|
|
160
|
-
if (
|
|
161
|
-
dimensions &&
|
|
162
|
-
dimensions.length > 0 &&
|
|
163
|
-
ctx.subscriptDepth < dimensions.length
|
|
164
|
-
) {
|
|
165
|
-
return {
|
|
166
|
-
code: String(dimensions[ctx.subscriptDepth]),
|
|
167
|
-
effects: [],
|
|
168
|
-
skipContinue: true,
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Array member fully subscripted -> return element bit width
|
|
173
|
-
if (
|
|
174
|
-
dimensions &&
|
|
175
|
-
dimensions.length > 0 &&
|
|
176
|
-
ctx.subscriptDepth >= dimensions.length
|
|
177
|
-
) {
|
|
178
|
-
return makeBitWidthResult(memberType, true, true); // isElement=true
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Non-array member -> return bit width
|
|
182
|
-
return makeBitWidthResult(memberType, true, false); // isElement=false
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Handle .length for string types.
|
|
187
|
-
*/
|
|
188
|
-
function handleStringLength(
|
|
189
|
-
ctx: PropertyContext,
|
|
190
|
-
typeInfo: TTypeInfo,
|
|
191
|
-
): PropertyResult {
|
|
192
|
-
// String array: arrayDimensions: [4, 65]
|
|
193
|
-
if (typeInfo.arrayDimensions && typeInfo.arrayDimensions.length > 1) {
|
|
194
|
-
if (ctx.subscriptDepth === 0) {
|
|
195
|
-
return {
|
|
196
|
-
code: String(typeInfo.arrayDimensions[0]),
|
|
197
|
-
effects: [],
|
|
198
|
-
skipContinue: false,
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
return makeStrlenResult(ctx.result, false);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Single string with cached length
|
|
205
|
-
if (ctx.currentIdentifier && ctx.lengthCache?.has(ctx.currentIdentifier)) {
|
|
206
|
-
return {
|
|
207
|
-
code: ctx.lengthCache.get(ctx.currentIdentifier)!,
|
|
208
|
-
effects: [],
|
|
209
|
-
skipContinue: false,
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Single string: strlen(str)
|
|
214
|
-
const target = ctx.currentIdentifier ?? ctx.result;
|
|
215
|
-
return makeStrlenResult(target, false);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Handle .length for fully subscripted array (getting element bit width).
|
|
220
|
-
*/
|
|
221
|
-
function handleFullySubscriptedArrayLength(
|
|
222
|
-
ctx: PropertyContext,
|
|
223
|
-
typeInfo: TTypeInfo,
|
|
224
|
-
): PropertyResult {
|
|
225
|
-
// ADR-017: Enum array element .length returns 32
|
|
226
|
-
if (typeInfo.isEnum) {
|
|
227
|
-
return { code: "32", effects: [], skipContinue: false };
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// ADR-045/Issue #136: String array element .length -> strlen
|
|
231
|
-
if (typeInfo.baseType.startsWith("string<") || typeInfo.isString) {
|
|
232
|
-
return makeStrlenResult(ctx.result, false);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// Try primitive type first
|
|
236
|
-
let elementBitWidth = getTypeBitWidth(typeInfo.baseType);
|
|
237
|
-
|
|
238
|
-
// Issue #201: Also check bitmap types
|
|
239
|
-
if (
|
|
240
|
-
elementBitWidth === 0 &&
|
|
241
|
-
typeInfo.isBitmap &&
|
|
242
|
-
typeInfo.bitmapTypeName &&
|
|
243
|
-
ctx.getBitmapBitWidth
|
|
244
|
-
) {
|
|
245
|
-
elementBitWidth = ctx.getBitmapBitWidth(typeInfo.bitmapTypeName) || 0;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
if (elementBitWidth > 0) {
|
|
249
|
-
return { code: String(elementBitWidth), effects: [], skipContinue: false };
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return {
|
|
253
|
-
code: `/* .length: unsupported element type ${typeInfo.baseType} */0`,
|
|
254
|
-
effects: [],
|
|
255
|
-
skipContinue: false,
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Generate code for .length property access.
|
|
261
|
-
*
|
|
262
|
-
* Handles:
|
|
263
|
-
* - Arrays: returns dimension at current subscript depth
|
|
264
|
-
* - Strings: returns strlen() call
|
|
265
|
-
* - Integers: returns bit width
|
|
266
|
-
* - Enums: returns 32 (default enum size)
|
|
267
|
-
* - main args.length: returns argc
|
|
268
|
-
*/
|
|
269
|
-
const generateLengthProperty = (ctx: PropertyContext): PropertyResult => {
|
|
270
|
-
// Special case: main function's args.length -> argc
|
|
271
|
-
if (ctx.mainArgsName && ctx.primaryId === ctx.mainArgsName) {
|
|
272
|
-
return { code: "argc", effects: [], skipContinue: false };
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// Check if we're accessing a struct member (cfg.magic.length)
|
|
276
|
-
const structResult = handleStructMemberLength(ctx);
|
|
277
|
-
if (structResult) {
|
|
278
|
-
return structResult;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Fall back to checking the current resolved identifier's type
|
|
282
|
-
const typeInfo = ctx.typeInfo;
|
|
283
|
-
|
|
284
|
-
if (!typeInfo) {
|
|
285
|
-
return {
|
|
286
|
-
code: `/* .length: unknown type for ${ctx.result} */0`,
|
|
287
|
-
effects: [],
|
|
288
|
-
skipContinue: false,
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// ADR-045: String type handling
|
|
293
|
-
if (typeInfo.isString) {
|
|
294
|
-
return handleStringLength(ctx, typeInfo);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Check for array with dimensions
|
|
298
|
-
const hasDimensions =
|
|
299
|
-
typeInfo.isArray &&
|
|
300
|
-
typeInfo.arrayDimensions &&
|
|
301
|
-
typeInfo.arrayDimensions.length > 0;
|
|
302
|
-
|
|
303
|
-
if (hasDimensions && ctx.subscriptDepth < typeInfo.arrayDimensions!.length) {
|
|
304
|
-
// ADR-036: Multi-dimensional array length (partial subscript)
|
|
305
|
-
return {
|
|
306
|
-
code: String(typeInfo.arrayDimensions![ctx.subscriptDepth]),
|
|
307
|
-
effects: [],
|
|
308
|
-
skipContinue: false,
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
if (hasDimensions && ctx.subscriptDepth >= typeInfo.arrayDimensions!.length) {
|
|
313
|
-
// Array fully subscripted -> return element bit width
|
|
314
|
-
return handleFullySubscriptedArrayLength(ctx, typeInfo);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
if (typeInfo.isArray) {
|
|
318
|
-
// Unknown length for array type
|
|
319
|
-
return {
|
|
320
|
-
code: `/* .length unknown for ${ctx.currentIdentifier} */0`,
|
|
321
|
-
effects: [],
|
|
322
|
-
skipContinue: false,
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
if (typeInfo.isEnum) {
|
|
327
|
-
// ADR-017: Enum types default to 32-bit width
|
|
328
|
-
return { code: "32", effects: [], skipContinue: false };
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// Integer bit width - return the compile-time constant
|
|
332
|
-
return { code: String(typeInfo.bitWidth), effects: [], skipContinue: false };
|
|
333
|
-
};
|
|
334
|
-
|
|
335
17
|
/**
|
|
336
18
|
* Generate code for .capacity property access.
|
|
337
19
|
*
|
|
@@ -394,7 +76,6 @@ const generateBitmapFieldAccess = (
|
|
|
394
76
|
|
|
395
77
|
// Export all generators
|
|
396
78
|
const accessGenerators = {
|
|
397
|
-
generateLengthProperty,
|
|
398
79
|
generateCapacityProperty,
|
|
399
80
|
generateSizeProperty,
|
|
400
81
|
generateBitmapFieldAccess,
|
|
@@ -386,7 +386,7 @@ const handleGlobalPrefix = (
|
|
|
386
386
|
const handleThisScopeLength = (
|
|
387
387
|
memberName: string,
|
|
388
388
|
tracking: ITrackingState,
|
|
389
|
-
|
|
389
|
+
_input: IGeneratorInput,
|
|
390
390
|
state: IGeneratorState,
|
|
391
391
|
orchestrator: IOrchestrator,
|
|
392
392
|
): boolean => {
|
|
@@ -531,8 +531,10 @@ const tryExplicitLengthProperty = (
|
|
|
531
531
|
};
|
|
532
532
|
|
|
533
533
|
/**
|
|
534
|
-
* Try handling property access (.
|
|
534
|
+
* Try handling property access (.capacity, .size, .bit_length, .byte_length, .element_count, .char_count).
|
|
535
535
|
* Returns true if handled.
|
|
536
|
+
*
|
|
537
|
+
* Note: .length was removed in favor of explicit properties (ADR-058).
|
|
536
538
|
*/
|
|
537
539
|
const tryPropertyAccess = (
|
|
538
540
|
memberName: string,
|
|
@@ -543,20 +545,13 @@ const tryPropertyAccess = (
|
|
|
543
545
|
orchestrator: IOrchestrator,
|
|
544
546
|
effects: TGeneratorEffect[],
|
|
545
547
|
): boolean => {
|
|
548
|
+
// ADR-058: .length is deprecated - use explicit properties instead
|
|
546
549
|
if (memberName === "length") {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
state,
|
|
552
|
-
orchestrator,
|
|
553
|
-
effects,
|
|
550
|
+
throw new Error(
|
|
551
|
+
`Error: '.length' on '${tracking.result}' is deprecated. Use explicit properties: ` +
|
|
552
|
+
`.bit_length (bit width), .byte_length (byte size), ` +
|
|
553
|
+
`.element_count (array size), or .char_count (string length)`,
|
|
554
554
|
);
|
|
555
|
-
if (lengthResult !== null) {
|
|
556
|
-
applyPropertyResult(tracking, lengthResult);
|
|
557
|
-
return true;
|
|
558
|
-
}
|
|
559
|
-
return false;
|
|
560
555
|
}
|
|
561
556
|
|
|
562
557
|
// ADR-058: Explicit length properties
|
|
@@ -607,264 +602,6 @@ const tryPropertyAccess = (
|
|
|
607
602
|
return false;
|
|
608
603
|
};
|
|
609
604
|
|
|
610
|
-
// ========================================================================
|
|
611
|
-
// Length Property
|
|
612
|
-
// ========================================================================
|
|
613
|
-
|
|
614
|
-
/**
|
|
615
|
-
* Context for .length property generation.
|
|
616
|
-
*/
|
|
617
|
-
interface ILengthContext {
|
|
618
|
-
result: string;
|
|
619
|
-
rootIdentifier: string | undefined;
|
|
620
|
-
resolvedIdentifier: string | undefined;
|
|
621
|
-
previousStructType: string | undefined;
|
|
622
|
-
previousMemberName: string | undefined;
|
|
623
|
-
subscriptDepth: number;
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
/**
|
|
627
|
-
* Generate .length property access.
|
|
628
|
-
* Returns null if not applicable (falls through to member access).
|
|
629
|
-
*/
|
|
630
|
-
const generateLengthProperty = (
|
|
631
|
-
ctx: ILengthContext,
|
|
632
|
-
input: IGeneratorInput,
|
|
633
|
-
state: IGeneratorState,
|
|
634
|
-
orchestrator: IOrchestrator,
|
|
635
|
-
effects: TGeneratorEffect[],
|
|
636
|
-
): string | null => {
|
|
637
|
-
// Special case: main function's args.length -> argc
|
|
638
|
-
if (state.mainArgsName && ctx.rootIdentifier === state.mainArgsName) {
|
|
639
|
-
return "argc";
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
// Check if we're accessing a struct member (cfg.magic.length)
|
|
643
|
-
if (ctx.previousStructType && ctx.previousMemberName) {
|
|
644
|
-
const fieldInfo = orchestrator.getStructFieldInfo(
|
|
645
|
-
ctx.previousStructType,
|
|
646
|
-
ctx.previousMemberName,
|
|
647
|
-
);
|
|
648
|
-
if (fieldInfo) {
|
|
649
|
-
return generateStructFieldLength(
|
|
650
|
-
ctx.result,
|
|
651
|
-
fieldInfo,
|
|
652
|
-
ctx.subscriptDepth,
|
|
653
|
-
input,
|
|
654
|
-
orchestrator,
|
|
655
|
-
effects,
|
|
656
|
-
);
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
// Fall back to checking the current resolved identifier's type
|
|
661
|
-
const typeInfo = ctx.resolvedIdentifier
|
|
662
|
-
? CodeGenState.getVariableTypeInfo(ctx.resolvedIdentifier)
|
|
663
|
-
: undefined;
|
|
664
|
-
|
|
665
|
-
if (!typeInfo) {
|
|
666
|
-
return `/* .length: unknown type for ${ctx.result} */0`;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
return generateTypeInfoLength(
|
|
670
|
-
ctx.result,
|
|
671
|
-
typeInfo,
|
|
672
|
-
ctx.subscriptDepth,
|
|
673
|
-
ctx.resolvedIdentifier,
|
|
674
|
-
input,
|
|
675
|
-
state,
|
|
676
|
-
effects,
|
|
677
|
-
);
|
|
678
|
-
};
|
|
679
|
-
|
|
680
|
-
/**
|
|
681
|
-
* Generate .length for a struct field.
|
|
682
|
-
*/
|
|
683
|
-
const generateStructFieldLength = (
|
|
684
|
-
result: string,
|
|
685
|
-
fieldInfo: { type: string; dimensions?: (number | string)[] },
|
|
686
|
-
subscriptDepth: number,
|
|
687
|
-
input: IGeneratorInput,
|
|
688
|
-
orchestrator: IOrchestrator,
|
|
689
|
-
effects: TGeneratorEffect[],
|
|
690
|
-
): string => {
|
|
691
|
-
const memberType = fieldInfo.type;
|
|
692
|
-
const dimensions = fieldInfo.dimensions;
|
|
693
|
-
const isStringField = TypeCheckUtils.isString(memberType);
|
|
694
|
-
|
|
695
|
-
if (dimensions?.length && dimensions.length > 1 && isStringField) {
|
|
696
|
-
if (subscriptDepth === 0) {
|
|
697
|
-
return String(dimensions[0]);
|
|
698
|
-
} else {
|
|
699
|
-
effects.push({ type: "include", header: "string" });
|
|
700
|
-
return `strlen(${result})`;
|
|
701
|
-
}
|
|
702
|
-
} else if (dimensions?.length === 1 && isStringField) {
|
|
703
|
-
effects.push({ type: "include", header: "string" });
|
|
704
|
-
return `strlen(${result})`;
|
|
705
|
-
} else if (
|
|
706
|
-
dimensions?.length &&
|
|
707
|
-
dimensions.length > 0 &&
|
|
708
|
-
subscriptDepth < dimensions.length
|
|
709
|
-
) {
|
|
710
|
-
return String(dimensions[subscriptDepth]);
|
|
711
|
-
} else if (
|
|
712
|
-
dimensions?.length &&
|
|
713
|
-
dimensions.length > 0 &&
|
|
714
|
-
subscriptDepth >= dimensions.length
|
|
715
|
-
) {
|
|
716
|
-
return getTypeBitWidth(memberType, input);
|
|
717
|
-
} else {
|
|
718
|
-
return getTypeBitWidth(memberType, input);
|
|
719
|
-
}
|
|
720
|
-
};
|
|
721
|
-
|
|
722
|
-
/**
|
|
723
|
-
* Generate .length from type info.
|
|
724
|
-
*/
|
|
725
|
-
const generateTypeInfoLength = (
|
|
726
|
-
result: string,
|
|
727
|
-
typeInfo: {
|
|
728
|
-
isString?: boolean;
|
|
729
|
-
isArray?: boolean;
|
|
730
|
-
isEnum?: boolean;
|
|
731
|
-
arrayDimensions?: (number | string)[];
|
|
732
|
-
baseType: string;
|
|
733
|
-
bitWidth?: number;
|
|
734
|
-
isBitmap?: boolean;
|
|
735
|
-
bitmapTypeName?: string;
|
|
736
|
-
},
|
|
737
|
-
subscriptDepth: number,
|
|
738
|
-
resolvedIdentifier: string | undefined,
|
|
739
|
-
input: IGeneratorInput,
|
|
740
|
-
state: IGeneratorState,
|
|
741
|
-
effects: TGeneratorEffect[],
|
|
742
|
-
): string => {
|
|
743
|
-
// ADR-045: String type handling
|
|
744
|
-
if (typeInfo.isString) {
|
|
745
|
-
return generateStringLength(
|
|
746
|
-
result,
|
|
747
|
-
typeInfo,
|
|
748
|
-
subscriptDepth,
|
|
749
|
-
resolvedIdentifier,
|
|
750
|
-
state,
|
|
751
|
-
);
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
// Non-string enum - always 32 bits
|
|
755
|
-
if (typeInfo.isEnum && !typeInfo.isArray) {
|
|
756
|
-
return "32";
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
// Non-string, non-enum, non-array - use bitWidth
|
|
760
|
-
if (!typeInfo.isArray) {
|
|
761
|
-
return String(typeInfo.bitWidth || 0);
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
// Array without dimensions - unknown length
|
|
765
|
-
const dims = typeInfo.arrayDimensions;
|
|
766
|
-
if (!dims || dims.length === 0) {
|
|
767
|
-
return `/* .length unknown for ${resolvedIdentifier} */0`;
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
// Array with subscript within bounds - return that dimension
|
|
771
|
-
if (subscriptDepth < dims.length) {
|
|
772
|
-
return String(dims[subscriptDepth]);
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
// Subscript past array bounds - return element type's length
|
|
776
|
-
return generateElementTypeLength(result, typeInfo, input, effects);
|
|
777
|
-
};
|
|
778
|
-
|
|
779
|
-
/**
|
|
780
|
-
* Generate .length for string types.
|
|
781
|
-
*/
|
|
782
|
-
const generateStringLength = (
|
|
783
|
-
result: string,
|
|
784
|
-
typeInfo: {
|
|
785
|
-
arrayDimensions?: (number | string)[];
|
|
786
|
-
},
|
|
787
|
-
subscriptDepth: number,
|
|
788
|
-
resolvedIdentifier: string | undefined,
|
|
789
|
-
state: IGeneratorState,
|
|
790
|
-
): string => {
|
|
791
|
-
const dims = typeInfo.arrayDimensions;
|
|
792
|
-
|
|
793
|
-
// String array (2D): first dimension is array size, second is string capacity
|
|
794
|
-
if (dims && dims.length > 1) {
|
|
795
|
-
return subscriptDepth === 0 ? String(dims[0]) : `strlen(${result})`;
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
// String array with subscript - use result which contains arr[index]
|
|
799
|
-
// This handles string<32>[5] parameters where subscriptDepth=1 after arr[index]
|
|
800
|
-
if (subscriptDepth > 0) {
|
|
801
|
-
return `strlen(${result})`;
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
// Simple string: check length cache first, then use strlen
|
|
805
|
-
if (resolvedIdentifier && state.lengthCache?.has(resolvedIdentifier)) {
|
|
806
|
-
return state.lengthCache.get(resolvedIdentifier)!;
|
|
807
|
-
}
|
|
808
|
-
return resolvedIdentifier
|
|
809
|
-
? `strlen(${resolvedIdentifier})`
|
|
810
|
-
: `strlen(${result})`;
|
|
811
|
-
};
|
|
812
|
-
|
|
813
|
-
/**
|
|
814
|
-
* Generate .length for array element types (subscript past bounds).
|
|
815
|
-
*/
|
|
816
|
-
const generateElementTypeLength = (
|
|
817
|
-
result: string,
|
|
818
|
-
typeInfo: {
|
|
819
|
-
isEnum?: boolean;
|
|
820
|
-
isString?: boolean;
|
|
821
|
-
baseType: string;
|
|
822
|
-
isBitmap?: boolean;
|
|
823
|
-
bitmapTypeName?: string;
|
|
824
|
-
},
|
|
825
|
-
input: IGeneratorInput,
|
|
826
|
-
effects: TGeneratorEffect[],
|
|
827
|
-
): string => {
|
|
828
|
-
// Enum element
|
|
829
|
-
if (typeInfo.isEnum) {
|
|
830
|
-
return "32";
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
// String element
|
|
834
|
-
if (TypeCheckUtils.isString(typeInfo.baseType) || typeInfo.isString) {
|
|
835
|
-
effects.push({ type: "include", header: "string" });
|
|
836
|
-
return `strlen(${result})`;
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
// Numeric/bitmap element - get bit width
|
|
840
|
-
let elementBitWidth = TYPE_WIDTH[typeInfo.baseType] || 0;
|
|
841
|
-
if (elementBitWidth === 0 && typeInfo.isBitmap && typeInfo.bitmapTypeName) {
|
|
842
|
-
elementBitWidth =
|
|
843
|
-
input.symbols!.bitmapBitWidth.get(typeInfo.bitmapTypeName) || 0;
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
if (elementBitWidth > 0) {
|
|
847
|
-
return String(elementBitWidth);
|
|
848
|
-
}
|
|
849
|
-
return `/* .length: unsupported element type ${typeInfo.baseType} */0`;
|
|
850
|
-
};
|
|
851
|
-
|
|
852
|
-
/**
|
|
853
|
-
* Get bit width for a type.
|
|
854
|
-
*/
|
|
855
|
-
const getTypeBitWidth = (typeName: string, input: IGeneratorInput): string => {
|
|
856
|
-
let bitWidth = TYPE_WIDTH[typeName] || C_TYPE_WIDTH[typeName] || 0;
|
|
857
|
-
if (bitWidth === 0 && input.symbolTable) {
|
|
858
|
-
const enumWidth = input.symbolTable.getEnumBitWidth(typeName);
|
|
859
|
-
if (enumWidth) bitWidth = enumWidth;
|
|
860
|
-
}
|
|
861
|
-
if (bitWidth > 0) {
|
|
862
|
-
return String(bitWidth);
|
|
863
|
-
} else {
|
|
864
|
-
return `/* .length: unsupported type ${typeName} */0`;
|
|
865
|
-
}
|
|
866
|
-
};
|
|
867
|
-
|
|
868
605
|
// ========================================================================
|
|
869
606
|
// ADR-058: Explicit Length Properties
|
|
870
607
|
// ========================================================================
|
|
@@ -1377,16 +1114,18 @@ const generateCharCountProperty = (
|
|
|
1377
1114
|
|
|
1378
1115
|
effects.push({ type: "include", header: "string" });
|
|
1379
1116
|
|
|
1380
|
-
// Check length cache first
|
|
1117
|
+
// Check length cache first (only for simple variable access, not indexed)
|
|
1381
1118
|
if (
|
|
1119
|
+
ctx.subscriptDepth === 0 &&
|
|
1382
1120
|
ctx.resolvedIdentifier &&
|
|
1383
1121
|
state.lengthCache?.has(ctx.resolvedIdentifier)
|
|
1384
1122
|
) {
|
|
1385
1123
|
return state.lengthCache.get(ctx.resolvedIdentifier)!;
|
|
1386
1124
|
}
|
|
1387
1125
|
|
|
1388
|
-
|
|
1389
|
-
|
|
1126
|
+
// Use ctx.result which contains the full expression including any subscripts
|
|
1127
|
+
// e.g., for arr[0].char_count, ctx.result is "arr[0]" not "arr"
|
|
1128
|
+
return `strlen(${ctx.result})`;
|
|
1390
1129
|
};
|
|
1391
1130
|
|
|
1392
1131
|
// ========================================================================
|
|
@@ -11,6 +11,9 @@ import IGeneratorOutput from "../IGeneratorOutput";
|
|
|
11
11
|
import IGeneratorInput from "../IGeneratorInput";
|
|
12
12
|
import IGeneratorState from "../IGeneratorState";
|
|
13
13
|
import IOrchestrator from "../IOrchestrator";
|
|
14
|
+
import TypeResolver from "../../TypeResolver";
|
|
15
|
+
import TYPE_MAP from "../../types/TYPE_MAP";
|
|
16
|
+
import CppModeHelper from "../../helpers/CppModeHelper";
|
|
14
17
|
|
|
15
18
|
/**
|
|
16
19
|
* Generate C code for a unary expression.
|
|
@@ -43,7 +46,16 @@ const generateUnaryExpr = (
|
|
|
43
46
|
// Determine the operator and generate output
|
|
44
47
|
if (text.startsWith("!")) return { code: `!${inner}`, effects: [] };
|
|
45
48
|
if (text.startsWith("-")) return { code: `-${inner}`, effects: [] };
|
|
46
|
-
if (text.startsWith("~"))
|
|
49
|
+
if (text.startsWith("~")) {
|
|
50
|
+
const innerType = TypeResolver.getUnaryExpressionType(
|
|
51
|
+
node.unaryExpression()!,
|
|
52
|
+
);
|
|
53
|
+
if (innerType && TypeResolver.isUnsignedType(innerType)) {
|
|
54
|
+
const cType = TYPE_MAP[innerType] ?? innerType;
|
|
55
|
+
return { code: CppModeHelper.cast(cType, `~${inner}`), effects: [] };
|
|
56
|
+
}
|
|
57
|
+
return { code: `~${inner}`, effects: [] };
|
|
58
|
+
}
|
|
47
59
|
if (text.startsWith("&")) return { code: `&${inner}`, effects: [] };
|
|
48
60
|
|
|
49
61
|
// Fallback (shouldn't happen with valid grammar)
|