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.
Files changed (37) hide show
  1. package/dist/index.js +387 -433
  2. package/dist/index.js.map +4 -4
  3. package/package.json +1 -1
  4. package/src/transpiler/Transpiler.ts +50 -29
  5. package/src/transpiler/__tests__/Transpiler.coverage.test.ts +2 -1
  6. package/src/transpiler/data/PathResolver.ts +10 -6
  7. package/src/transpiler/data/__tests__/PathResolver.test.ts +32 -0
  8. package/src/transpiler/logic/analysis/PassByValueAnalyzer.ts +1 -12
  9. package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +239 -0
  10. package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +414 -0
  11. package/src/transpiler/logic/analysis/__tests__/StructFieldAnalyzer.test.ts +15 -75
  12. package/src/transpiler/logic/analysis/__tests__/runAnalyzers.test.ts +3 -15
  13. package/src/transpiler/logic/analysis/runAnalyzers.ts +11 -2
  14. package/src/transpiler/logic/analysis/types/ISignedShiftError.ts +15 -0
  15. package/src/transpiler/logic/symbols/SymbolUtils.ts +4 -1
  16. package/src/transpiler/logic/symbols/__tests__/SymbolUtils.test.ts +10 -11
  17. package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +4 -4
  18. package/src/transpiler/logic/symbols/cpp/__tests__/CppResolver.integration.test.ts +4 -4
  19. package/src/transpiler/output/codegen/CodeGenerator.ts +12 -4
  20. package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +3 -3
  21. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +26 -26
  22. package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +12 -11
  23. package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +21 -21
  24. package/src/transpiler/output/codegen/generators/expressions/AccessExprGenerator.ts +7 -326
  25. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +14 -275
  26. package/src/transpiler/output/codegen/generators/expressions/UnaryExprGenerator.ts +13 -1
  27. package/src/transpiler/output/codegen/generators/expressions/__tests__/AccessExprGenerator.test.ts +0 -573
  28. package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +8 -439
  29. package/src/transpiler/output/codegen/generators/expressions/__tests__/UnaryExprGenerator.test.ts +196 -0
  30. package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +7 -2
  31. package/src/transpiler/output/codegen/helpers/TypedefParamParser.ts +34 -0
  32. package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +48 -0
  33. package/src/transpiler/output/codegen/helpers/__tests__/TypedefParamParser.test.ts +88 -0
  34. package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +15 -2
  35. package/src/transpiler/output/headers/__tests__/BaseHeaderGenerator.test.ts +87 -0
  36. package/src/utils/ExpressionUtils.ts +51 -0
  37. 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 expressions:
5
- * - .length property for arrays, strings, and integers
6
- * - .capacity property for strings
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
- input: IGeneratorInput,
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 (.length, .capacity, .size, .bit_length, .byte_length, .element_count, .char_count).
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
- const ctx = createExplicitLengthContext(tracking, rootIdentifier);
548
- const lengthResult = generateLengthProperty(
549
- ctx,
550
- input,
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
- const target = ctx.resolvedIdentifier ?? ctx.result;
1389
- return `strlen(${target})`;
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("~")) return { code: `~${inner}`, effects: [] };
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)