c-next 0.2.16 → 0.2.18

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 (118) hide show
  1. package/README.md +18 -2
  2. package/dist/index.js +8897 -6260
  3. package/dist/index.js.map +4 -4
  4. package/grammar/CNext.g4 +12 -0
  5. package/package.json +4 -2
  6. package/src/transpiler/Transpiler.ts +376 -48
  7. package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
  8. package/src/transpiler/__tests__/compileCommandsDiscovery.integration.test.ts +94 -0
  9. package/src/transpiler/__tests__/externalSymbolRecovery.integration.test.ts +215 -0
  10. package/src/transpiler/logic/__tests__/detectAssemblySyntax.test.ts +116 -0
  11. package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +65 -10
  12. package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +186 -14
  13. package/src/transpiler/logic/analysis/MixedTypeCategoryAnalyzer.ts +408 -0
  14. package/src/transpiler/logic/analysis/PassByValueAnalyzer.ts +16 -97
  15. package/src/transpiler/logic/analysis/ReturnPathAnalyzer.ts +171 -0
  16. package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +124 -12
  17. package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +200 -0
  18. package/src/transpiler/logic/analysis/__tests__/InitializationAnalyzer.test.ts +386 -1
  19. package/src/transpiler/logic/analysis/__tests__/MixedTypeCategoryAnalyzer.test.ts +346 -0
  20. package/src/transpiler/logic/analysis/__tests__/ReturnPathAnalyzer.test.ts +232 -0
  21. package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +211 -0
  22. package/src/transpiler/logic/analysis/runAnalyzers.ts +23 -2
  23. package/src/transpiler/logic/analysis/types/IMixedTypeCategoryError.ts +17 -0
  24. package/src/transpiler/logic/analysis/types/IReturnPathError.ts +12 -0
  25. package/src/transpiler/logic/detectAssemblySyntax.ts +80 -0
  26. package/src/transpiler/logic/parser/grammar/CNext.interp +4 -1
  27. package/src/transpiler/logic/parser/grammar/CNext.tokens +169 -167
  28. package/src/transpiler/logic/parser/grammar/CNextLexer.interp +4 -1
  29. package/src/transpiler/logic/parser/grammar/CNextLexer.tokens +169 -167
  30. package/src/transpiler/logic/parser/grammar/CNextLexer.ts +545 -541
  31. package/src/transpiler/logic/parser/grammar/CNextListener.ts +11 -0
  32. package/src/transpiler/logic/parser/grammar/CNextParser.ts +1318 -1178
  33. package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +7 -0
  34. package/src/transpiler/logic/preprocessor/CompileCommandsReader.ts +332 -0
  35. package/src/transpiler/logic/preprocessor/ExternalDeclarationOracle.ts +202 -0
  36. package/src/transpiler/logic/preprocessor/Preprocessor.ts +24 -6
  37. package/src/transpiler/logic/preprocessor/ToolchainDetector.ts +42 -1
  38. package/src/transpiler/logic/preprocessor/__tests__/CompileCommandsReader.test.ts +216 -0
  39. package/src/transpiler/logic/preprocessor/__tests__/ExternalDeclarationOracle.test.ts +153 -0
  40. package/src/transpiler/logic/preprocessor/__tests__/Preprocessor.test.ts +43 -18
  41. package/src/transpiler/logic/preprocessor/__tests__/ToolchainDetector.crossCompiler.test.ts +75 -0
  42. package/src/transpiler/logic/preprocessor/__tests__/fixtures/oracle/dependent.h +7 -0
  43. package/src/transpiler/logic/preprocessor/__tests__/fixtures/oracle/predecessor.h +5 -0
  44. package/src/transpiler/logic/preprocessor/types/ICompileCommandsResult.ts +14 -0
  45. package/src/transpiler/logic/preprocessor/types/IPreprocessOptions.ts +17 -0
  46. package/src/transpiler/logic/symbols/SymbolTable.ts +62 -2
  47. package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +55 -4
  48. package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +15 -2
  49. package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +64 -50
  50. package/src/transpiler/output/MisraSuppressionUtils.ts +52 -0
  51. package/src/transpiler/output/__tests__/MisraSuppressionUtils.test.ts +67 -0
  52. package/src/transpiler/output/codegen/CodeGenerator.ts +196 -98
  53. package/src/transpiler/output/codegen/TypeResolver.ts +148 -0
  54. package/src/transpiler/output/codegen/TypeValidator.ts +179 -81
  55. package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +165 -17
  56. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +163 -35
  57. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
  58. package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +93 -0
  59. package/src/transpiler/output/codegen/__tests__/TypeValidator.alwaysTrueLoop.test.ts +91 -0
  60. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +97 -14
  61. package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +13 -0
  62. package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +1 -1
  63. package/src/transpiler/output/codegen/assignment/handlers/AccessPatternHandlers.ts +20 -12
  64. package/src/transpiler/output/codegen/assignment/handlers/ArrayHandlers.ts +424 -22
  65. package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +31 -18
  66. package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +7 -8
  67. package/src/transpiler/output/codegen/assignment/handlers/RegisterHandlers.ts +6 -8
  68. package/src/transpiler/output/codegen/assignment/handlers/RegisterUtils.ts +12 -10
  69. package/src/transpiler/output/codegen/assignment/handlers/SimpleHandler.ts +3 -7
  70. package/src/transpiler/output/codegen/assignment/handlers/SpecialHandlers.ts +7 -9
  71. package/src/transpiler/output/codegen/assignment/handlers/StringHandlers.ts +12 -10
  72. package/src/transpiler/output/codegen/assignment/handlers/__tests__/ArrayHandlers.test.ts +479 -11
  73. package/src/transpiler/output/codegen/generators/IOrchestrator.ts +3 -0
  74. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +26 -7
  75. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +86 -0
  76. package/src/transpiler/output/codegen/generators/expressions/BinaryExprGenerator.ts +43 -24
  77. package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +76 -58
  78. package/src/transpiler/output/codegen/generators/expressions/ExpressionGenerator.ts +9 -2
  79. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +26 -13
  80. package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +44 -0
  81. package/src/transpiler/output/codegen/generators/expressions/__tests__/ExpressionGenerator.test.ts +82 -1
  82. package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +129 -1
  83. package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +64 -8
  84. package/src/transpiler/output/codegen/generators/statements/__tests__/ControlFlowGenerator.test.ts +8 -5
  85. package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +30 -2
  86. package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +17 -3
  87. package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +17 -4
  88. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +240 -42
  89. package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +0 -21
  90. package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +60 -39
  91. package/src/transpiler/output/codegen/helpers/TypeRegistrationEngine.ts +170 -36
  92. package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +37 -39
  93. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +19 -0
  94. package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +117 -0
  95. package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +94 -2
  96. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +322 -9
  97. package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +0 -64
  98. package/src/transpiler/output/codegen/helpers/__tests__/TypeRegistrationEngine.test.ts +101 -0
  99. package/src/transpiler/output/codegen/helpers/__tests__/VariableDeclHelper.test.ts +29 -5
  100. package/src/transpiler/output/codegen/subscript/SubscriptClassifier.ts +30 -11
  101. package/src/transpiler/output/codegen/subscript/TSubscriptKind.ts +1 -1
  102. package/src/transpiler/output/codegen/subscript/__tests__/SubscriptClassifier.test.ts +38 -6
  103. package/src/transpiler/output/codegen/types/ICallbackTypeInfo.ts +2 -1
  104. package/src/transpiler/output/codegen/types/IParameterInput.ts +7 -0
  105. package/src/transpiler/output/headers/BaseHeaderGenerator.ts +8 -0
  106. package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +140 -24
  107. package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +280 -0
  108. package/src/transpiler/output/headers/generators/IHeaderTypeInput.ts +13 -0
  109. package/src/transpiler/output/headers/generators/generateStructHeader.ts +48 -28
  110. package/src/transpiler/state/CodeGenState.ts +91 -22
  111. package/src/transpiler/state/__tests__/CodeGenState.test.ts +253 -11
  112. package/src/transpiler/types/ICachedFileEntry.ts +7 -0
  113. package/src/utils/LiteralUtils.ts +23 -0
  114. package/src/utils/__tests__/LiteralUtils.test.ts +101 -0
  115. package/src/utils/cache/CacheManager.ts +13 -2
  116. package/src/utils/cache/__tests__/CacheManager.test.ts +18 -1
  117. package/src/utils/constants/TypeConstants.ts +13 -0
  118. package/src/utils/types/IParameterSymbol.ts +1 -0
@@ -13,7 +13,6 @@
13
13
  */
14
14
 
15
15
  import * as Parser from "../../../logic/parser/grammar/CNextParser.js";
16
- import FormatUtils from "../../../../utils/FormatUtils.js";
17
16
  import StringUtils from "../../../../utils/StringUtils.js";
18
17
  import CodeGenState from "../../../state/CodeGenState.js";
19
18
 
@@ -98,6 +97,21 @@ class StringDeclHelper {
98
97
  isConst: boolean,
99
98
  callbacks: IStringDeclCallbacks,
100
99
  ): IStringDeclResult {
100
+ // Issue #1029: Check for string array in arrayType syntax first
101
+ // For `string<32>[4] items`, the structure is: arrayType -> stringType arrayTypeDimension+
102
+ const arrayTypeCtx = typeCtx.arrayType?.();
103
+ if (arrayTypeCtx?.stringType?.()) {
104
+ return StringDeclHelper._generateStringArrayFromArrayType(
105
+ name,
106
+ arrayTypeCtx,
107
+ expression,
108
+ arrayDims,
109
+ modifiers,
110
+ isConst,
111
+ callbacks,
112
+ );
113
+ }
114
+
101
115
  const stringCtx = typeCtx.stringType();
102
116
  if (!stringCtx) {
103
117
  return { code: "", handled: false };
@@ -129,6 +143,182 @@ class StringDeclHelper {
129
143
  }
130
144
  }
131
145
 
146
+ /**
147
+ * Issue #1029: Generate string array declaration from arrayType syntax.
148
+ * Handles: string<32>[4] items -> char items[4][33] = {0};
149
+ */
150
+ private static _generateStringArrayFromArrayType(
151
+ name: string,
152
+ arrayTypeCtx: Parser.ArrayTypeContext,
153
+ expression: Parser.ExpressionContext | null,
154
+ trailingArrayDims: Parser.ArrayDimensionContext[],
155
+ modifiers: IStringDeclModifiers,
156
+ isConst: boolean,
157
+ callbacks: IStringDeclCallbacks,
158
+ ): IStringDeclResult {
159
+ const stringCtx = arrayTypeCtx.stringType()!;
160
+ const intLiteral = stringCtx.INTEGER_LITERAL();
161
+ if (!intLiteral) {
162
+ // Unsized string array - not supported
163
+ throw new Error(
164
+ "Error: String arrays require explicit capacity, e.g., string<64>[4]",
165
+ );
166
+ }
167
+
168
+ const capacity = Number.parseInt(intLiteral.getText(), 10);
169
+ // Ensure string.h is included for strncpy operations
170
+ callbacks.requireStringInclude();
171
+
172
+ const {
173
+ extern,
174
+ const: constMod,
175
+ atomic,
176
+ volatile: volatileMod,
177
+ } = modifiers;
178
+
179
+ // Build array dimensions from arrayType (e.g., [4] from string<32>[4])
180
+ let arrayDimStr = "";
181
+ for (const dim of arrayTypeCtx.arrayTypeDimension()) {
182
+ const sizeExpr = dim.expression();
183
+ if (sizeExpr) {
184
+ arrayDimStr += `[${sizeExpr.getText()}]`;
185
+ } else {
186
+ arrayDimStr += "[]";
187
+ }
188
+ }
189
+
190
+ // Add any trailing dimensions from variable declaration
191
+ arrayDimStr += callbacks.generateArrayDimensions(trailingArrayDims);
192
+
193
+ // Add string capacity dimension
194
+ arrayDimStr += `[${capacity + 1}]`;
195
+
196
+ let decl = `${extern}${constMod}${atomic}${volatileMod}char ${name}${arrayDimStr}`;
197
+
198
+ // Track as local array
199
+ CodeGenState.localArrays.add(name);
200
+
201
+ // No initializer - zero-initialize
202
+ if (!expression) {
203
+ return { code: `${decl} = {0};`, handled: true };
204
+ }
205
+
206
+ // Reset array init tracking and generate initializer
207
+ CodeGenState.lastArrayInitCount = 0;
208
+ CodeGenState.lastArrayFillValue = undefined;
209
+ const initValue = callbacks.generateExpression(expression);
210
+
211
+ // Check if it was an array initializer
212
+ const isArrayInit =
213
+ CodeGenState.lastArrayInitCount > 0 ||
214
+ CodeGenState.lastArrayFillValue !== undefined;
215
+
216
+ if (!isArrayInit) {
217
+ throw new Error(
218
+ `Error: String array initialization from variables not supported`,
219
+ );
220
+ }
221
+
222
+ // Validate element count if declared size is available
223
+ const declaredSize =
224
+ StringDeclHelper._getArrayTypeDeclaredSize(arrayTypeCtx);
225
+ if (declaredSize !== null) {
226
+ const isFillAll = CodeGenState.lastArrayFillValue !== undefined;
227
+ const elementCount = CodeGenState.lastArrayInitCount;
228
+
229
+ if (!isFillAll && elementCount !== declaredSize) {
230
+ throw new Error(
231
+ `Error: Array size mismatch - declared [${declaredSize}] but got ${elementCount} elements`,
232
+ );
233
+ }
234
+ }
235
+
236
+ // Handle fill-all expansion if needed
237
+ const finalInitValue = StringDeclHelper._expandFillAllForArrayType(
238
+ initValue,
239
+ arrayTypeCtx,
240
+ );
241
+
242
+ // MISRA C:2012 Rules 9.3/9.4 - String literals don't fill all inner array bytes,
243
+ // but C standard guarantees zero-initialization of remaining elements
244
+ const suppression =
245
+ "// cppcheck-suppress misra-c2012-9.3\n// cppcheck-suppress misra-c2012-9.4\n";
246
+ return {
247
+ code: `${suppression}${decl} = ${finalInitValue};`,
248
+ handled: true,
249
+ };
250
+ }
251
+
252
+ /**
253
+ * Get the numeric size from the first arrayTypeDimension, or null if not numeric.
254
+ * Used by arrayType-based string arrays (string<N>[M]).
255
+ */
256
+ private static _getArrayTypeDeclaredSize(
257
+ arrayTypeCtx: Parser.ArrayTypeContext,
258
+ ): number | null {
259
+ const dims = arrayTypeCtx.arrayTypeDimension();
260
+ if (dims.length === 0) {
261
+ return null;
262
+ }
263
+ const firstDimExpr = dims[0].expression();
264
+ return StringDeclHelper._parseNumericSize(firstDimExpr);
265
+ }
266
+
267
+ /**
268
+ * Expand fill-all syntax for arrayType-based string arrays.
269
+ * Delegates to the common fill-all expansion logic with size from arrayType.
270
+ */
271
+ private static _expandFillAllForArrayType(
272
+ initValue: string,
273
+ arrayTypeCtx: Parser.ArrayTypeContext,
274
+ ): string {
275
+ const declaredSize =
276
+ StringDeclHelper._getArrayTypeDeclaredSize(arrayTypeCtx);
277
+ return StringDeclHelper._expandFillAll(initValue, declaredSize);
278
+ }
279
+
280
+ /**
281
+ * Common fill-all expansion logic shared between arrayType and arrayDimension paths.
282
+ */
283
+ private static _expandFillAll(
284
+ initValue: string,
285
+ declaredSize: number | null,
286
+ ): string {
287
+ const fillVal = CodeGenState.lastArrayFillValue;
288
+ if (fillVal === undefined) {
289
+ return initValue;
290
+ }
291
+
292
+ // Empty string fill doesn't need expansion (C handles {""} correctly)
293
+ if (fillVal === '""') {
294
+ return initValue;
295
+ }
296
+
297
+ if (declaredSize === null) {
298
+ return initValue;
299
+ }
300
+
301
+ const elements = new Array<string>(declaredSize).fill(fillVal);
302
+ return `{${elements.join(", ")}}`;
303
+ }
304
+
305
+ /**
306
+ * Parse a numeric size from an expression, or return null if not numeric.
307
+ * Shared helper to eliminate duplicate parsing logic.
308
+ */
309
+ private static _parseNumericSize(
310
+ expr: Parser.ExpressionContext | null | undefined,
311
+ ): number | null {
312
+ if (!expr) {
313
+ return null;
314
+ }
315
+ const sizeText = expr.getText();
316
+ if (!/^\d+$/.exec(sizeText)) {
317
+ return null;
318
+ }
319
+ return Number.parseInt(sizeText, 10);
320
+ }
321
+
132
322
  /**
133
323
  * Generate bounded string declaration (string<N>).
134
324
  */
@@ -207,24 +397,52 @@ class StringDeclHelper {
207
397
  );
208
398
  }
209
399
 
210
- // Validate and generate simple assignment
211
- StringDeclHelper._validateStringInit(
212
- expression.getText(),
400
+ // Validate and check if it's a literal or variable
401
+ const exprText = expression.getText();
402
+ const isLiteral = StringDeclHelper._validateStringInit(
403
+ exprText,
213
404
  capacity,
214
405
  callbacks,
215
406
  );
216
- const code = `${extern}${constMod}char ${name}[${capacity + 1}] = ${callbacks.generateExpression(expression)};`;
217
- return { code, handled: true };
407
+
408
+ if (isLiteral) {
409
+ // String literal: can use direct initialization
410
+ const code = `${extern}${constMod}char ${name}[${capacity + 1}] = ${callbacks.generateExpression(expression)};`;
411
+ return { code, handled: true };
412
+ }
413
+
414
+ // String variable: cannot use C array initialization, so declare empty and
415
+ // copy. Issue #1044: use the same bounded copy as the reassignment path
416
+ // (strncpy + explicit null terminator via StringUtils.copyWithNull) rather
417
+ // than an unbounded strcpy, which flawfinder flags as CWE-120.
418
+ // Issue #1030: string-to-string initialization
419
+ if (!CodeGenState.inFunctionBody) {
420
+ throw new Error(
421
+ `Error: String initialization from variable cannot be used at global scope. ` +
422
+ `Move the declaration inside a function, or use an empty initializer and assign later.`,
423
+ );
424
+ }
425
+
426
+ const srcExpr = callbacks.generateExpression(expression);
427
+ // Issue #1037: continuation lines carry no indent of their own — the block
428
+ // emitter (CodeGenerator.generateBlock) prefixes every line.
429
+ const lines: string[] = [];
430
+ lines.push(
431
+ `${constMod}char ${name}[${capacity + 1}] = "";`,
432
+ StringUtils.copyWithNull(name, srcExpr, capacity),
433
+ );
434
+ return { code: lines.join("\n"), handled: true };
218
435
  }
219
436
 
220
437
  /**
221
438
  * Validate string initialization (literal length and variable capacity)
439
+ * Returns true if the expression is a string literal, false if it's a variable.
222
440
  */
223
441
  private static _validateStringInit(
224
442
  exprText: string,
225
443
  capacity: number,
226
444
  callbacks: IStringDeclCallbacks,
227
- ): void {
445
+ ): boolean {
228
446
  // Validate string literal fits capacity
229
447
  if (exprText.startsWith('"') && exprText.endsWith('"')) {
230
448
  const content = StringUtils.literalLength(exprText);
@@ -233,6 +451,7 @@ class StringDeclHelper {
233
451
  `Error: String literal (${content} chars) exceeds string<${capacity}> capacity`,
234
452
  );
235
453
  }
454
+ return true; // Is a literal
236
455
  }
237
456
 
238
457
  // Check for string variable assignment
@@ -242,6 +461,7 @@ class StringDeclHelper {
242
461
  `Error: Cannot assign string<${srcCapacity}> to string<${capacity}> (potential truncation)`,
243
462
  );
244
463
  }
464
+ return false; // Is a variable (not a literal)
245
465
  }
246
466
 
247
467
  /**
@@ -373,47 +593,25 @@ class StringDeclHelper {
373
593
  /**
374
594
  * Expand fill-all syntax if needed.
375
595
  * ["Hello"*] with size 3 -> {"Hello", "Hello", "Hello"}
596
+ * Delegates to the common fill-all expansion logic with size from arrayDimension.
376
597
  */
377
598
  private static _expandFillAllIfNeeded(
378
599
  initValue: string,
379
600
  arrayDims: Parser.ArrayDimensionContext[],
380
601
  ): string {
381
- const fillVal = CodeGenState.lastArrayFillValue;
382
- if (fillVal === undefined) {
383
- return initValue;
384
- }
385
-
386
- // Empty string fill doesn't need expansion (C handles {""} correctly)
387
- if (fillVal === '""') {
388
- return initValue;
389
- }
390
-
391
602
  const declaredSize = StringDeclHelper._getFirstDimNumericSize(arrayDims);
392
- if (declaredSize === null) {
393
- return initValue;
394
- }
395
-
396
- const elements = new Array<string>(declaredSize).fill(fillVal);
397
- return `{${elements.join(", ")}}`;
603
+ return StringDeclHelper._expandFillAll(initValue, declaredSize);
398
604
  }
399
605
 
400
606
  /**
401
607
  * Get the numeric size from the first array dimension, or null if not numeric.
608
+ * Used by arrayDimension-based string arrays (string<N> arr[M]).
402
609
  */
403
610
  private static _getFirstDimNumericSize(
404
611
  arrayDims: Parser.ArrayDimensionContext[],
405
612
  ): number | null {
406
613
  const firstDimExpr = arrayDims[0]?.expression();
407
- if (!firstDimExpr) {
408
- return null;
409
- }
410
-
411
- const sizeText = firstDimExpr.getText();
412
- if (!/^\d+$/.exec(sizeText)) {
413
- return null;
414
- }
415
-
416
- return Number.parseInt(sizeText, 10);
614
+ return StringDeclHelper._parseNumericSize(firstDimExpr);
417
615
  }
418
616
 
419
617
  /**
@@ -442,14 +640,14 @@ class StringDeclHelper {
442
640
  );
443
641
  }
444
642
 
445
- // Generate safe concatenation code
446
- const indent = FormatUtils.indent(CodeGenState.indentLevel);
643
+ // Generate safe concatenation code. Issue #1037: continuation lines carry
644
+ // no indent of their own — the block emitter prefixes every line.
447
645
  const lines: string[] = [];
448
646
  lines.push(
449
647
  `${constMod}char ${name}[${capacity + 1}] = "";`,
450
- `${indent}strncpy(${name}, ${concatOps.left}, ${capacity});`,
451
- `${indent}strncat(${name}, ${concatOps.right}, ${capacity} - strlen(${name}));`,
452
- `${indent}${name}[${capacity}] = ${C_NULL_CHAR};`,
648
+ `strncpy(${name}, ${concatOps.left}, ${capacity});`,
649
+ `strncat(${name}, ${concatOps.right}, ${capacity} - strlen(${name}));`,
650
+ `${name}[${capacity}] = ${C_NULL_CHAR};`,
453
651
  );
454
652
  return { code: lines.join("\n"), handled: true };
455
653
  }
@@ -493,13 +691,13 @@ class StringDeclHelper {
493
691
  );
494
692
  }
495
693
 
496
- // Generate safe substring extraction code
497
- const indent = FormatUtils.indent(CodeGenState.indentLevel);
694
+ // Generate safe substring extraction code. Issue #1037: continuation lines
695
+ // carry no indent of their own — the block emitter prefixes every line.
498
696
  const lines: string[] = [];
499
697
  lines.push(
500
698
  `${constMod}char ${name}[${capacity + 1}] = "";`,
501
- `${indent}strncpy(${name}, ${substringOps.source} + ${substringOps.start}, ${substringOps.length});`,
502
- `${indent}${name}[${substringOps.length}] = ${C_NULL_CHAR};`,
699
+ `strncpy(${name}, ${substringOps.source} + ${substringOps.start}, ${substringOps.length});`,
700
+ `${name}[${substringOps.length}] = ${C_NULL_CHAR};`,
503
701
  );
504
702
  return { code: lines.join("\n"), handled: true };
505
703
  }
@@ -87,27 +87,6 @@ class SymbolLookupHelper {
87
87
  return symbols.some((sym) => sym.kind === "namespace");
88
88
  }
89
89
 
90
- /**
91
- * Check if a type name is from a C++ header.
92
- * Issue #304: Used to determine whether to use {} or {0} for initialization.
93
- * C++ types with constructors may fail with {0} but work with {}.
94
- */
95
- static isCppType(
96
- symbolTable: ISymbolTable | null | undefined,
97
- typeName: string,
98
- ): boolean {
99
- if (!symbolTable) return false;
100
-
101
- const symbols = symbolTable.getOverloads(typeName);
102
- const cppTypeKinds = new Set<TSymbolKind>(["struct", "class", "type"]);
103
-
104
- return symbols.some(
105
- (sym) =>
106
- sym.sourceLanguage === ESourceLanguage.Cpp &&
107
- cppTypeKinds.has(sym.kind),
108
- );
109
- }
110
-
111
90
  /**
112
91
  * Check if a function is a C-Next function (uses pass-by-reference semantics).
113
92
  * Returns true if the function is found in symbol table as C-Next.
@@ -29,6 +29,19 @@ interface ITypeGenerationDeps {
29
29
  validateCrossScopeVisibility: (scope: string, member: string) => void;
30
30
  }
31
31
 
32
+ /**
33
+ * Common interface for type contexts that share the same type accessors.
34
+ * Both TypeContext and ArrayTypeContext have these methods.
35
+ */
36
+ interface ITypeAccessors {
37
+ primitiveType(): Parser.PrimitiveTypeContext | null;
38
+ userType(): Parser.UserTypeContext | null;
39
+ stringType(): Parser.StringTypeContext | null;
40
+ scopedType(): Parser.ScopedTypeContext | null;
41
+ qualifiedType(): Parser.QualifiedTypeContext | null;
42
+ globalType(): Parser.GlobalTypeContext | null;
43
+ }
44
+
32
45
  class TypeGenerationHelper {
33
46
  /**
34
47
  * Generate C type for a primitive type.
@@ -154,41 +167,35 @@ class TypeGenerationHelper {
154
167
  }
155
168
 
156
169
  /**
157
- * Full type generation using all dependencies.
158
- * This is the main entry point that handles all type contexts.
170
+ * Dispatch type generation for contexts that share common type accessors.
171
+ * Handles scoped, qualified, global, primitive, string, and user types.
172
+ * Used by both bare type contexts and array element type contexts.
173
+ *
174
+ * @returns The resolved C type string, or null if no matching type accessor found
159
175
  */
160
- static generate(ctx: Parser.TypeContext, deps: ITypeGenerationDeps): string {
161
- // Primitive type
162
- if (ctx.primitiveType()) {
163
- const type = ctx.primitiveType()!.getText();
164
- const result = TypeGenerationHelper.generatePrimitiveType(type);
165
- // Note: caller is responsible for handling the include
166
- return result.cType;
167
- }
168
-
169
- // Bounded string type
170
- if (ctx.stringType()) {
176
+ private static dispatchTypeGeneration(
177
+ accessors: ITypeAccessors,
178
+ deps: ITypeGenerationDeps,
179
+ ): string | null {
180
+ if (accessors.stringType()) {
171
181
  return TypeGenerationHelper.generateStringType();
172
182
  }
173
183
 
174
- // Scoped type (this.Type)
175
- if (ctx.scopedType()) {
176
- const typeName = ctx.scopedType()!.IDENTIFIER().getText();
184
+ if (accessors.scopedType()) {
185
+ const typeName = accessors.scopedType()!.IDENTIFIER().getText();
177
186
  return TypeGenerationHelper.generateScopedType(
178
187
  typeName,
179
188
  deps.currentScope,
180
189
  );
181
190
  }
182
191
 
183
- // Global type (global.Type)
184
- if (ctx.globalType()) {
185
- const typeName = ctx.globalType()!.IDENTIFIER().getText();
192
+ if (accessors.globalType()) {
193
+ const typeName = accessors.globalType()!.IDENTIFIER().getText();
186
194
  return TypeGenerationHelper.generateGlobalType(typeName);
187
195
  }
188
196
 
189
- // Qualified type (Scope.Type or Namespace::Type)
190
- if (ctx.qualifiedType()) {
191
- const identifiers = ctx.qualifiedType()!.IDENTIFIER();
197
+ if (accessors.qualifiedType()) {
198
+ const identifiers = accessors.qualifiedType()!.IDENTIFIER();
192
199
  const identifierNames = identifiers.map((id) => id.getText());
193
200
  const isCpp = deps.isCppScopeSymbol(identifierNames[0]);
194
201
  return TypeGenerationHelper.generateQualifiedType(
@@ -198,30 +205,44 @@ class TypeGenerationHelper {
198
205
  );
199
206
  }
200
207
 
201
- // User type
202
- if (ctx.userType()) {
203
- const typeName = ctx.userType()!.getText();
208
+ if (accessors.primitiveType()) {
209
+ const type = accessors.primitiveType()!.getText();
210
+ return TYPE_MAP[type] || type;
211
+ }
212
+
213
+ if (accessors.userType()) {
214
+ const typeName = accessors.userType()!.getText();
215
+ // ADR-046: cstring maps to char* for C library interop
216
+ if (typeName === "cstring") {
217
+ return "char*";
218
+ }
204
219
  const needsStruct = deps.checkNeedsStructKeyword(typeName);
205
220
  return TypeGenerationHelper.generateUserType(typeName, needsStruct);
206
221
  }
207
222
 
208
- // Array type
223
+ return null;
224
+ }
225
+
226
+ /**
227
+ * Full type generation using all dependencies.
228
+ * This is the main entry point that handles all type contexts.
229
+ */
230
+ static generate(ctx: Parser.TypeContext, deps: ITypeGenerationDeps): string {
231
+ // Array type - dispatch on the element type
209
232
  if (ctx.arrayType()) {
210
233
  const arrCtx = ctx.arrayType()!;
211
- // String arrays have base type "char"
212
- if (arrCtx.stringType()) {
213
- return "char";
234
+ const result = TypeGenerationHelper.dispatchTypeGeneration(arrCtx, deps);
235
+ if (result !== null) {
236
+ return result;
214
237
  }
215
- const primitiveText = arrCtx.primitiveType()?.getText() ?? null;
216
- const userTypeName = arrCtx.userType()?.getText() ?? null;
217
- const needsStruct = userTypeName
218
- ? deps.checkNeedsStructKeyword(userTypeName)
219
- : false;
220
- return TypeGenerationHelper.generateArrayBaseType(
221
- primitiveText,
222
- userTypeName,
223
- needsStruct,
224
- );
238
+ // Fallback for array types without recognized element type
239
+ return ctx.getText();
240
+ }
241
+
242
+ // Non-array types - dispatch directly
243
+ const result = TypeGenerationHelper.dispatchTypeGeneration(ctx, deps);
244
+ if (result !== null) {
245
+ return result;
225
246
  }
226
247
 
227
248
  // Void or fallback