c-next 0.2.16 → 0.2.17

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 (56) hide show
  1. package/README.md +16 -0
  2. package/dist/index.js +1347 -414
  3. package/dist/index.js.map +3 -3
  4. package/grammar/CNext.g4 +4 -0
  5. package/package.json +5 -1
  6. package/src/transpiler/Transpiler.ts +90 -22
  7. package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
  8. package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +57 -10
  9. package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +186 -14
  10. package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +124 -12
  11. package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +200 -0
  12. package/src/transpiler/logic/analysis/__tests__/InitializationAnalyzer.test.ts +386 -1
  13. package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +211 -0
  14. package/src/transpiler/logic/parser/grammar/CNext.interp +1 -1
  15. package/src/transpiler/logic/parser/grammar/CNextParser.ts +154 -86
  16. package/src/transpiler/logic/symbols/SymbolTable.ts +17 -2
  17. package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +6 -4
  18. package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +15 -2
  19. package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +64 -50
  20. package/src/transpiler/output/codegen/CodeGenerator.ts +151 -94
  21. package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +165 -17
  22. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +150 -34
  23. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
  24. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +11 -0
  25. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +26 -7
  26. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +86 -0
  27. package/src/transpiler/output/codegen/generators/expressions/BinaryExprGenerator.ts +43 -24
  28. package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +48 -43
  29. package/src/transpiler/output/codegen/generators/expressions/ExpressionGenerator.ts +9 -2
  30. package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +44 -0
  31. package/src/transpiler/output/codegen/generators/expressions/__tests__/ExpressionGenerator.test.ts +82 -1
  32. package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +17 -3
  33. package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +17 -4
  34. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +227 -32
  35. package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +0 -21
  36. package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +60 -39
  37. package/src/transpiler/output/codegen/helpers/TypeRegistrationEngine.ts +170 -36
  38. package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +37 -39
  39. package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +117 -0
  40. package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +94 -2
  41. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +268 -1
  42. package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +0 -64
  43. package/src/transpiler/output/codegen/helpers/__tests__/TypeRegistrationEngine.test.ts +101 -0
  44. package/src/transpiler/output/codegen/helpers/__tests__/VariableDeclHelper.test.ts +29 -5
  45. package/src/transpiler/output/codegen/types/ICallbackTypeInfo.ts +2 -1
  46. package/src/transpiler/output/codegen/types/IParameterInput.ts +7 -0
  47. package/src/transpiler/output/headers/BaseHeaderGenerator.ts +8 -0
  48. package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +75 -0
  49. package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +280 -0
  50. package/src/transpiler/output/headers/generators/IHeaderTypeInput.ts +13 -0
  51. package/src/transpiler/output/headers/generators/generateStructHeader.ts +48 -28
  52. package/src/transpiler/state/CodeGenState.ts +71 -6
  53. package/src/transpiler/state/__tests__/CodeGenState.test.ts +253 -11
  54. package/src/utils/LiteralUtils.ts +23 -0
  55. package/src/utils/__tests__/LiteralUtils.test.ts +101 -0
  56. package/src/utils/types/IParameterSymbol.ts +1 -0
@@ -98,6 +98,21 @@ class StringDeclHelper {
98
98
  isConst: boolean,
99
99
  callbacks: IStringDeclCallbacks,
100
100
  ): IStringDeclResult {
101
+ // Issue #1029: Check for string array in arrayType syntax first
102
+ // For `string<32>[4] items`, the structure is: arrayType -> stringType arrayTypeDimension+
103
+ const arrayTypeCtx = typeCtx.arrayType?.();
104
+ if (arrayTypeCtx?.stringType?.()) {
105
+ return StringDeclHelper._generateStringArrayFromArrayType(
106
+ name,
107
+ arrayTypeCtx,
108
+ expression,
109
+ arrayDims,
110
+ modifiers,
111
+ isConst,
112
+ callbacks,
113
+ );
114
+ }
115
+
101
116
  const stringCtx = typeCtx.stringType();
102
117
  if (!stringCtx) {
103
118
  return { code: "", handled: false };
@@ -129,6 +144,182 @@ class StringDeclHelper {
129
144
  }
130
145
  }
131
146
 
147
+ /**
148
+ * Issue #1029: Generate string array declaration from arrayType syntax.
149
+ * Handles: string<32>[4] items -> char items[4][33] = {0};
150
+ */
151
+ private static _generateStringArrayFromArrayType(
152
+ name: string,
153
+ arrayTypeCtx: Parser.ArrayTypeContext,
154
+ expression: Parser.ExpressionContext | null,
155
+ trailingArrayDims: Parser.ArrayDimensionContext[],
156
+ modifiers: IStringDeclModifiers,
157
+ isConst: boolean,
158
+ callbacks: IStringDeclCallbacks,
159
+ ): IStringDeclResult {
160
+ const stringCtx = arrayTypeCtx.stringType()!;
161
+ const intLiteral = stringCtx.INTEGER_LITERAL();
162
+ if (!intLiteral) {
163
+ // Unsized string array - not supported
164
+ throw new Error(
165
+ "Error: String arrays require explicit capacity, e.g., string<64>[4]",
166
+ );
167
+ }
168
+
169
+ const capacity = Number.parseInt(intLiteral.getText(), 10);
170
+ // Ensure string.h is included for strncpy operations
171
+ callbacks.requireStringInclude();
172
+
173
+ const {
174
+ extern,
175
+ const: constMod,
176
+ atomic,
177
+ volatile: volatileMod,
178
+ } = modifiers;
179
+
180
+ // Build array dimensions from arrayType (e.g., [4] from string<32>[4])
181
+ let arrayDimStr = "";
182
+ for (const dim of arrayTypeCtx.arrayTypeDimension()) {
183
+ const sizeExpr = dim.expression();
184
+ if (sizeExpr) {
185
+ arrayDimStr += `[${sizeExpr.getText()}]`;
186
+ } else {
187
+ arrayDimStr += "[]";
188
+ }
189
+ }
190
+
191
+ // Add any trailing dimensions from variable declaration
192
+ arrayDimStr += callbacks.generateArrayDimensions(trailingArrayDims);
193
+
194
+ // Add string capacity dimension
195
+ arrayDimStr += `[${capacity + 1}]`;
196
+
197
+ let decl = `${extern}${constMod}${atomic}${volatileMod}char ${name}${arrayDimStr}`;
198
+
199
+ // Track as local array
200
+ CodeGenState.localArrays.add(name);
201
+
202
+ // No initializer - zero-initialize
203
+ if (!expression) {
204
+ return { code: `${decl} = {0};`, handled: true };
205
+ }
206
+
207
+ // Reset array init tracking and generate initializer
208
+ CodeGenState.lastArrayInitCount = 0;
209
+ CodeGenState.lastArrayFillValue = undefined;
210
+ const initValue = callbacks.generateExpression(expression);
211
+
212
+ // Check if it was an array initializer
213
+ const isArrayInit =
214
+ CodeGenState.lastArrayInitCount > 0 ||
215
+ CodeGenState.lastArrayFillValue !== undefined;
216
+
217
+ if (!isArrayInit) {
218
+ throw new Error(
219
+ `Error: String array initialization from variables not supported`,
220
+ );
221
+ }
222
+
223
+ // Validate element count if declared size is available
224
+ const declaredSize =
225
+ StringDeclHelper._getArrayTypeDeclaredSize(arrayTypeCtx);
226
+ if (declaredSize !== null) {
227
+ const isFillAll = CodeGenState.lastArrayFillValue !== undefined;
228
+ const elementCount = CodeGenState.lastArrayInitCount;
229
+
230
+ if (!isFillAll && elementCount !== declaredSize) {
231
+ throw new Error(
232
+ `Error: Array size mismatch - declared [${declaredSize}] but got ${elementCount} elements`,
233
+ );
234
+ }
235
+ }
236
+
237
+ // Handle fill-all expansion if needed
238
+ const finalInitValue = StringDeclHelper._expandFillAllForArrayType(
239
+ initValue,
240
+ arrayTypeCtx,
241
+ );
242
+
243
+ // MISRA C:2012 Rules 9.3/9.4 - String literals don't fill all inner array bytes,
244
+ // but C standard guarantees zero-initialization of remaining elements
245
+ const suppression =
246
+ "// cppcheck-suppress misra-c2012-9.3\n// cppcheck-suppress misra-c2012-9.4\n";
247
+ return {
248
+ code: `${suppression}${decl} = ${finalInitValue};`,
249
+ handled: true,
250
+ };
251
+ }
252
+
253
+ /**
254
+ * Get the numeric size from the first arrayTypeDimension, or null if not numeric.
255
+ * Used by arrayType-based string arrays (string<N>[M]).
256
+ */
257
+ private static _getArrayTypeDeclaredSize(
258
+ arrayTypeCtx: Parser.ArrayTypeContext,
259
+ ): number | null {
260
+ const dims = arrayTypeCtx.arrayTypeDimension();
261
+ if (dims.length === 0) {
262
+ return null;
263
+ }
264
+ const firstDimExpr = dims[0].expression();
265
+ return StringDeclHelper._parseNumericSize(firstDimExpr);
266
+ }
267
+
268
+ /**
269
+ * Expand fill-all syntax for arrayType-based string arrays.
270
+ * Delegates to the common fill-all expansion logic with size from arrayType.
271
+ */
272
+ private static _expandFillAllForArrayType(
273
+ initValue: string,
274
+ arrayTypeCtx: Parser.ArrayTypeContext,
275
+ ): string {
276
+ const declaredSize =
277
+ StringDeclHelper._getArrayTypeDeclaredSize(arrayTypeCtx);
278
+ return StringDeclHelper._expandFillAll(initValue, declaredSize);
279
+ }
280
+
281
+ /**
282
+ * Common fill-all expansion logic shared between arrayType and arrayDimension paths.
283
+ */
284
+ private static _expandFillAll(
285
+ initValue: string,
286
+ declaredSize: number | null,
287
+ ): string {
288
+ const fillVal = CodeGenState.lastArrayFillValue;
289
+ if (fillVal === undefined) {
290
+ return initValue;
291
+ }
292
+
293
+ // Empty string fill doesn't need expansion (C handles {""} correctly)
294
+ if (fillVal === '""') {
295
+ return initValue;
296
+ }
297
+
298
+ if (declaredSize === null) {
299
+ return initValue;
300
+ }
301
+
302
+ const elements = new Array<string>(declaredSize).fill(fillVal);
303
+ return `{${elements.join(", ")}}`;
304
+ }
305
+
306
+ /**
307
+ * Parse a numeric size from an expression, or return null if not numeric.
308
+ * Shared helper to eliminate duplicate parsing logic.
309
+ */
310
+ private static _parseNumericSize(
311
+ expr: Parser.ExpressionContext | null | undefined,
312
+ ): number | null {
313
+ if (!expr) {
314
+ return null;
315
+ }
316
+ const sizeText = expr.getText();
317
+ if (!/^\d+$/.exec(sizeText)) {
318
+ return null;
319
+ }
320
+ return Number.parseInt(sizeText, 10);
321
+ }
322
+
132
323
  /**
133
324
  * Generate bounded string declaration (string<N>).
134
325
  */
@@ -207,24 +398,48 @@ class StringDeclHelper {
207
398
  );
208
399
  }
209
400
 
210
- // Validate and generate simple assignment
211
- StringDeclHelper._validateStringInit(
212
- expression.getText(),
401
+ // Validate and check if it's a literal or variable
402
+ const exprText = expression.getText();
403
+ const isLiteral = StringDeclHelper._validateStringInit(
404
+ exprText,
213
405
  capacity,
214
406
  callbacks,
215
407
  );
216
- const code = `${extern}${constMod}char ${name}[${capacity + 1}] = ${callbacks.generateExpression(expression)};`;
217
- return { code, handled: true };
408
+
409
+ if (isLiteral) {
410
+ // String literal: can use direct initialization
411
+ const code = `${extern}${constMod}char ${name}[${capacity + 1}] = ${callbacks.generateExpression(expression)};`;
412
+ return { code, handled: true };
413
+ }
414
+
415
+ // String variable: need strcpy (cannot use array initialization in C)
416
+ // Issue #1030: string-to-string initialization
417
+ if (!CodeGenState.inFunctionBody) {
418
+ throw new Error(
419
+ `Error: String initialization from variable cannot be used at global scope. ` +
420
+ `Move the declaration inside a function, or use an empty initializer and assign later.`,
421
+ );
422
+ }
423
+
424
+ const srcExpr = callbacks.generateExpression(expression);
425
+ const indent = FormatUtils.indent(CodeGenState.indentLevel);
426
+ const lines: string[] = [];
427
+ lines.push(
428
+ `${constMod}char ${name}[${capacity + 1}] = "";`,
429
+ `${indent}strcpy(${name}, ${srcExpr});`,
430
+ );
431
+ return { code: lines.join("\n"), handled: true };
218
432
  }
219
433
 
220
434
  /**
221
435
  * Validate string initialization (literal length and variable capacity)
436
+ * Returns true if the expression is a string literal, false if it's a variable.
222
437
  */
223
438
  private static _validateStringInit(
224
439
  exprText: string,
225
440
  capacity: number,
226
441
  callbacks: IStringDeclCallbacks,
227
- ): void {
442
+ ): boolean {
228
443
  // Validate string literal fits capacity
229
444
  if (exprText.startsWith('"') && exprText.endsWith('"')) {
230
445
  const content = StringUtils.literalLength(exprText);
@@ -233,6 +448,7 @@ class StringDeclHelper {
233
448
  `Error: String literal (${content} chars) exceeds string<${capacity}> capacity`,
234
449
  );
235
450
  }
451
+ return true; // Is a literal
236
452
  }
237
453
 
238
454
  // Check for string variable assignment
@@ -242,6 +458,7 @@ class StringDeclHelper {
242
458
  `Error: Cannot assign string<${srcCapacity}> to string<${capacity}> (potential truncation)`,
243
459
  );
244
460
  }
461
+ return false; // Is a variable (not a literal)
245
462
  }
246
463
 
247
464
  /**
@@ -373,47 +590,25 @@ class StringDeclHelper {
373
590
  /**
374
591
  * Expand fill-all syntax if needed.
375
592
  * ["Hello"*] with size 3 -> {"Hello", "Hello", "Hello"}
593
+ * Delegates to the common fill-all expansion logic with size from arrayDimension.
376
594
  */
377
595
  private static _expandFillAllIfNeeded(
378
596
  initValue: string,
379
597
  arrayDims: Parser.ArrayDimensionContext[],
380
598
  ): 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
599
  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(", ")}}`;
600
+ return StringDeclHelper._expandFillAll(initValue, declaredSize);
398
601
  }
399
602
 
400
603
  /**
401
604
  * Get the numeric size from the first array dimension, or null if not numeric.
605
+ * Used by arrayDimension-based string arrays (string<N> arr[M]).
402
606
  */
403
607
  private static _getFirstDimNumericSize(
404
608
  arrayDims: Parser.ArrayDimensionContext[],
405
609
  ): number | null {
406
610
  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);
611
+ return StringDeclHelper._parseNumericSize(firstDimExpr);
417
612
  }
418
613
 
419
614
  /**
@@ -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