c-next 0.1.65 → 0.1.66

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c-next",
3
- "version": "0.1.65",
3
+ "version": "0.1.66",
4
4
  "description": "A safer C for embedded systems development. Transpiles to clean, readable C.",
5
5
  "packageManager": "npm@11.9.0",
6
6
  "type": "module",
@@ -118,5 +118,9 @@
118
118
  "*.ts": [
119
119
  "oxlint --fix"
120
120
  ]
121
+ },
122
+ "volta": {
123
+ "node": "24.13.1",
124
+ "npm": "11.8.0"
121
125
  }
122
126
  }
@@ -463,4 +463,183 @@ describe("TSymbolAdapter", () => {
463
463
  expect(symbolTable.getStructFieldType("Point", "x")).toBe("i32");
464
464
  });
465
465
  });
466
+
467
+ describe("enum member resolution in array dimensions", () => {
468
+ it("resolves unqualified enum member in variable array dimension", () => {
469
+ const enumSym: IEnumSymbol = {
470
+ kind: ESymbolKind.Enum,
471
+ name: "EColor",
472
+ sourceFile: "test.cnx",
473
+ sourceLine: 1,
474
+ sourceLanguage: ESourceLanguage.CNext,
475
+ isExported: true,
476
+ members: new Map([
477
+ ["RED", 0],
478
+ ["GREEN", 1],
479
+ ["BLUE", 2],
480
+ ["COUNT", 3],
481
+ ]),
482
+ };
483
+
484
+ const variable: IVariableSymbol = {
485
+ kind: ESymbolKind.Variable,
486
+ name: "DATA",
487
+ sourceFile: "test.cnx",
488
+ sourceLine: 10,
489
+ sourceLanguage: ESourceLanguage.CNext,
490
+ isExported: true,
491
+ type: "u8",
492
+ isConst: true,
493
+ isAtomic: false,
494
+ isArray: true,
495
+ arrayDimensions: ["COUNT"], // Unqualified enum member
496
+ };
497
+
498
+ const result = TSymbolAdapter.toISymbols(
499
+ [enumSym, variable],
500
+ symbolTable,
501
+ );
502
+
503
+ // Find the variable symbol
504
+ const varSym = result.find(
505
+ (s) => s.kind === ESymbolKind.Variable && s.name === "DATA",
506
+ );
507
+ expect(varSym).toBeDefined();
508
+ expect(varSym!.arrayDimensions).toEqual(["EColor_COUNT"]);
509
+ });
510
+
511
+ it("resolves enum member in function parameter array dimension", () => {
512
+ const enumSym: IEnumSymbol = {
513
+ kind: ESymbolKind.Enum,
514
+ name: "Size",
515
+ sourceFile: "test.cnx",
516
+ sourceLine: 1,
517
+ sourceLanguage: ESourceLanguage.CNext,
518
+ isExported: true,
519
+ members: new Map([
520
+ ["SMALL", 10],
521
+ ["MEDIUM", 20],
522
+ ["LARGE", 30],
523
+ ]),
524
+ };
525
+
526
+ const funcSym: IFunctionSymbol = {
527
+ kind: ESymbolKind.Function,
528
+ name: "process",
529
+ sourceFile: "test.cnx",
530
+ sourceLine: 5,
531
+ sourceLanguage: ESourceLanguage.CNext,
532
+ isExported: true,
533
+ returnType: "void",
534
+ visibility: "public",
535
+ parameters: [
536
+ {
537
+ name: "buffer",
538
+ type: "u8",
539
+ isConst: false,
540
+ isArray: true,
541
+ arrayDimensions: ["MEDIUM"], // Unqualified enum member
542
+ },
543
+ ],
544
+ };
545
+
546
+ const result = TSymbolAdapter.toISymbols([enumSym, funcSym], symbolTable);
547
+
548
+ // Find the function symbol
549
+ const funcResult = result.find(
550
+ (s) => s.kind === ESymbolKind.Function && s.name === "process",
551
+ );
552
+ expect(funcResult).toBeDefined();
553
+ expect(funcResult!.parameters).toBeDefined();
554
+ expect(funcResult!.parameters![0].arrayDimensions).toEqual([
555
+ "Size_MEDIUM",
556
+ ]);
557
+ });
558
+
559
+ it("does not resolve ambiguous enum member (exists in multiple enums)", () => {
560
+ const enum1: IEnumSymbol = {
561
+ kind: ESymbolKind.Enum,
562
+ name: "EColor",
563
+ sourceFile: "test.cnx",
564
+ sourceLine: 1,
565
+ sourceLanguage: ESourceLanguage.CNext,
566
+ isExported: true,
567
+ members: new Map([["COUNT", 3]]),
568
+ };
569
+
570
+ const enum2: IEnumSymbol = {
571
+ kind: ESymbolKind.Enum,
572
+ name: "ESize",
573
+ sourceFile: "test.cnx",
574
+ sourceLine: 5,
575
+ sourceLanguage: ESourceLanguage.CNext,
576
+ isExported: true,
577
+ members: new Map([
578
+ ["COUNT", 5], // Same member name in different enum
579
+ ]),
580
+ };
581
+
582
+ const variable: IVariableSymbol = {
583
+ kind: ESymbolKind.Variable,
584
+ name: "DATA",
585
+ sourceFile: "test.cnx",
586
+ sourceLine: 10,
587
+ sourceLanguage: ESourceLanguage.CNext,
588
+ isExported: true,
589
+ type: "u8",
590
+ isConst: true,
591
+ isAtomic: false,
592
+ isArray: true,
593
+ arrayDimensions: ["COUNT"], // Ambiguous - exists in both enums
594
+ };
595
+
596
+ const result = TSymbolAdapter.toISymbols(
597
+ [enum1, enum2, variable],
598
+ symbolTable,
599
+ );
600
+
601
+ // Find the variable symbol - dimension should NOT be resolved
602
+ const varSym = result.find(
603
+ (s) => s.kind === ESymbolKind.Variable && s.name === "DATA",
604
+ );
605
+ expect(varSym).toBeDefined();
606
+ expect(varSym!.arrayDimensions).toEqual(["COUNT"]); // Left as-is
607
+ });
608
+
609
+ it("preserves numeric array dimensions", () => {
610
+ const enumSym: IEnumSymbol = {
611
+ kind: ESymbolKind.Enum,
612
+ name: "EColor",
613
+ sourceFile: "test.cnx",
614
+ sourceLine: 1,
615
+ sourceLanguage: ESourceLanguage.CNext,
616
+ isExported: true,
617
+ members: new Map([["COUNT", 3]]),
618
+ };
619
+
620
+ const variable: IVariableSymbol = {
621
+ kind: ESymbolKind.Variable,
622
+ name: "DATA",
623
+ sourceFile: "test.cnx",
624
+ sourceLine: 10,
625
+ sourceLanguage: ESourceLanguage.CNext,
626
+ isExported: true,
627
+ type: "u8",
628
+ isConst: true,
629
+ isAtomic: false,
630
+ isArray: true,
631
+ arrayDimensions: [256], // Numeric dimension
632
+ };
633
+
634
+ const result = TSymbolAdapter.toISymbols(
635
+ [enumSym, variable],
636
+ symbolTable,
637
+ );
638
+
639
+ const varSym = result.find(
640
+ (s) => s.kind === ESymbolKind.Variable && s.name === "DATA",
641
+ );
642
+ expect(varSym!.arrayDimensions).toEqual(["256"]);
643
+ });
644
+ });
466
645
  });
@@ -40,6 +40,9 @@ class TSymbolAdapter {
40
40
  * @returns Array of flat ISymbol objects for Pipeline consumption
41
41
  */
42
42
  static toISymbols(symbols: TSymbol[], symbolTable: SymbolTable): ISymbol[] {
43
+ // First pass: Build enum member lookup for array dimension resolution
44
+ const enumMemberLookup = TSymbolAdapter.buildEnumMemberLookup(symbols);
45
+
43
46
  const result: ISymbol[] = [];
44
47
 
45
48
  for (const symbol of symbols) {
@@ -54,10 +57,12 @@ class TSymbolAdapter {
54
57
  result.push(TSymbolAdapter.convertStruct(symbol, symbolTable));
55
58
  break;
56
59
  case ESymbolKind.Function:
57
- result.push(...TSymbolAdapter.convertFunction(symbol));
60
+ result.push(
61
+ ...TSymbolAdapter.convertFunction(symbol, enumMemberLookup),
62
+ );
58
63
  break;
59
64
  case ESymbolKind.Variable:
60
- result.push(TSymbolAdapter.convertVariable(symbol));
65
+ result.push(TSymbolAdapter.convertVariable(symbol, enumMemberLookup));
61
66
  break;
62
67
  case ESymbolKind.Register:
63
68
  result.push(...TSymbolAdapter.convertRegister(symbol));
@@ -71,6 +76,57 @@ class TSymbolAdapter {
71
76
  return result;
72
77
  }
73
78
 
79
+ /**
80
+ * Build a lookup map from enum member names to their fully-qualified enum names.
81
+ * Used to resolve unqualified enum members in array dimensions.
82
+ *
83
+ * If a member name exists in multiple enums, it's marked as ambiguous (null value)
84
+ * and will not be resolved (the header will fail to compile, surfacing the issue).
85
+ */
86
+ private static buildEnumMemberLookup(
87
+ symbols: TSymbol[],
88
+ ): Map<string, string | null> {
89
+ const lookup = new Map<string, string | null>();
90
+
91
+ for (const symbol of symbols) {
92
+ if (symbol.kind !== ESymbolKind.Enum) continue;
93
+
94
+ for (const memberName of symbol.members.keys()) {
95
+ if (lookup.has(memberName)) {
96
+ // Ambiguous: member exists in multiple enums
97
+ lookup.set(memberName, null);
98
+ } else {
99
+ lookup.set(memberName, symbol.name);
100
+ }
101
+ }
102
+ }
103
+
104
+ return lookup;
105
+ }
106
+
107
+ /**
108
+ * Resolve an array dimension string, adding enum prefix if it's an unqualified enum member.
109
+ */
110
+ private static resolveArrayDimension(
111
+ dim: number | string,
112
+ enumMemberLookup: Map<string, string | null>,
113
+ ): string {
114
+ // Numeric dimensions don't need resolution
115
+ if (typeof dim === "number") {
116
+ return String(dim);
117
+ }
118
+
119
+ // Check if this is an unqualified enum member
120
+ const enumName = enumMemberLookup.get(dim);
121
+ if (enumName) {
122
+ // Found in exactly one enum - add the prefix
123
+ return `${enumName}_${dim}`;
124
+ }
125
+
126
+ // Not an enum member, or ambiguous - return as-is
127
+ return dim;
128
+ }
129
+
74
130
  /**
75
131
  * Convert IBitmapSymbol to ISymbol + BitmapField symbols.
76
132
  */
@@ -180,21 +236,27 @@ class TSymbolAdapter {
180
236
 
181
237
  /**
182
238
  * Convert IFunctionSymbol to ISymbol + parameter symbols for hover support.
239
+ * Resolves unqualified enum members in parameter array dimensions.
183
240
  */
184
- private static convertFunction(func: IFunctionSymbol): ISymbol[] {
241
+ private static convertFunction(
242
+ func: IFunctionSymbol,
243
+ enumMemberLookup: Map<string, string | null>,
244
+ ): ISymbol[] {
185
245
  const result: ISymbol[] = [];
186
246
 
187
247
  // Build parameter types for signature
188
248
  const paramTypes = func.parameters.map((p) => p.type);
189
249
  const signature = `${func.returnType} ${func.name}(${paramTypes.join(", ")})`;
190
250
 
191
- // Build parameter info for header generation
251
+ // Build parameter info for header generation, resolving enum members in dimensions
192
252
  const parameters = func.parameters.map((p) => ({
193
253
  name: p.name,
194
254
  type: p.type,
195
255
  isConst: p.isConst,
196
256
  isArray: p.isArray,
197
- arrayDimensions: p.arrayDimensions,
257
+ arrayDimensions: p.arrayDimensions?.map((dim) =>
258
+ TSymbolAdapter.resolveArrayDimension(dim, enumMemberLookup),
259
+ ),
198
260
  isAutoConst: p.isAutoConst,
199
261
  }));
200
262
 
@@ -233,10 +295,16 @@ class TSymbolAdapter {
233
295
 
234
296
  /**
235
297
  * Convert IVariableSymbol to ISymbol.
298
+ * Resolves unqualified enum members in array dimensions.
236
299
  */
237
- private static convertVariable(variable: IVariableSymbol): ISymbol {
238
- // Convert dimensions to string dimensions for ISymbol
239
- const arrayDimensions = variable.arrayDimensions?.map(String);
300
+ private static convertVariable(
301
+ variable: IVariableSymbol,
302
+ enumMemberLookup: Map<string, string | null>,
303
+ ): ISymbol {
304
+ // Convert dimensions to string dimensions, resolving enum member references
305
+ const arrayDimensions = variable.arrayDimensions?.map((dim) =>
306
+ TSymbolAdapter.resolveArrayDimension(dim, enumMemberLookup),
307
+ );
240
308
 
241
309
  // Get first dimension for legacy size field (only if numeric)
242
310
  const firstDim = variable.arrayDimensions?.[0];
@@ -134,6 +134,9 @@ import TypeGenerationHelper from "./helpers/TypeGenerationHelper";
134
134
  import CastValidator from "./helpers/CastValidator";
135
135
  // Global state for code generation (simplifies debugging, eliminates DI complexity)
136
136
  import CodeGenState from "./CodeGenState";
137
+ // Unified parameter generation (Phase 1)
138
+ import ParameterInputAdapter from "./helpers/ParameterInputAdapter";
139
+ import ParameterSignatureBuilder from "./helpers/ParameterSignatureBuilder";
137
140
  // Extracted resolvers that use CodeGenState
138
141
  import SizeofResolver from "./resolution/SizeofResolver";
139
142
  import EnumTypeResolver from "./resolution/EnumTypeResolver";
@@ -802,30 +805,54 @@ export default class CodeGenerator implements IOrchestrator {
802
805
  }
803
806
 
804
807
  // Issue #137: Check for array element access (e.g., names[0], arr[i])
808
+ return this._isArrayAccessStringExpression(text);
809
+ }
810
+
811
+ /**
812
+ * Check if array access expression evaluates to a string.
813
+ * Extracted from isStringExpression to reduce cognitive complexity.
814
+ */
815
+ private _isArrayAccessStringExpression(text: string): boolean {
805
816
  // Pattern: identifier[expression] or identifier[expression][expression]...
806
817
  // BUT NOT if accessing .length/.capacity/.size (those return numbers, not strings)
807
818
  const arrayAccessMatch = /^([a-zA-Z_]\w*)\[/.exec(text);
808
- if (arrayAccessMatch) {
809
- // ADR-045: String properties return numeric values, not strings
810
- if (
811
- text.endsWith(".length") ||
812
- text.endsWith(".capacity") ||
813
- text.endsWith(".size")
814
- ) {
815
- return false;
816
- }
817
- const arrayName = arrayAccessMatch[1];
818
- const typeInfo = CodeGenState.typeRegistry.get(arrayName);
819
- // Check if base type is a string type
820
- if (
821
- typeInfo?.isString ||
822
- (typeInfo?.baseType && TypeCheckUtils.isString(typeInfo.baseType))
823
- ) {
824
- return true;
825
- }
819
+ if (!arrayAccessMatch) {
820
+ return false;
826
821
  }
827
822
 
828
- return false;
823
+ // ADR-045: String properties return numeric values, not strings
824
+ if (
825
+ text.endsWith(".length") ||
826
+ text.endsWith(".capacity") ||
827
+ text.endsWith(".size")
828
+ ) {
829
+ return false;
830
+ }
831
+
832
+ const arrayName = arrayAccessMatch[1];
833
+ const typeInfo = CodeGenState.typeRegistry.get(arrayName);
834
+ if (!typeInfo) {
835
+ return false;
836
+ }
837
+
838
+ // Check if it's an ARRAY OF STRINGS (not a single string being indexed)
839
+ // A single string<50> has arrayDimensions=[51] (just the char buffer)
840
+ // An array of strings string<50>[10] has arrayDimensions=[10, 51]
841
+ // Single string indexing (e.g., userName[i]) returns a char, not a string
842
+ // Array of strings indexing (e.g., names[0]) returns a string
843
+ if (typeInfo.isString) {
844
+ // For strings, only treat as string expression if it's an array of strings
845
+ // (arrayDimensions.length > 1 means it's string<N>[M], not just string<N>)
846
+ const dims = typeInfo.arrayDimensions;
847
+ return Array.isArray(dims) && dims.length > 1;
848
+ }
849
+
850
+ // Non-string array with string base type
851
+ return Boolean(
852
+ typeInfo.isArray &&
853
+ typeInfo.baseType &&
854
+ TypeCheckUtils.isString(typeInfo.baseType),
855
+ );
829
856
  }
830
857
 
831
858
  /**
@@ -1505,7 +1532,9 @@ export default class CodeGenerator implements IOrchestrator {
1505
1532
  return `${constMod}${p.type} ${p.name}${p.arrayDims}`;
1506
1533
  } else if (p.isPointer) {
1507
1534
  // ADR-006: Non-array, non-callback parameters become pointers
1508
- return `${constMod}${p.type}*`;
1535
+ // In C++ mode, use reference (&) instead of pointer (*)
1536
+ const ptrOrRef = this.isCppMode() ? "&" : "*";
1537
+ return `${constMod}${p.type}${ptrOrRef}`;
1509
1538
  } else {
1510
1539
  // ADR-029: Callback parameters are already function pointers
1511
1540
  return `${p.type}`;
@@ -2373,9 +2402,13 @@ export default class CodeGenerator implements IOrchestrator {
2373
2402
  */
2374
2403
  private addGeneratedHelpers(output: string[]): void {
2375
2404
  if (CodeGenState.needsFloatStaticAssert) {
2405
+ // Use static_assert for C++ (standard), _Static_assert for C11
2406
+ const assertKeyword = this.isCppMode()
2407
+ ? "static_assert"
2408
+ : "_Static_assert";
2376
2409
  output.push(
2377
- '_Static_assert(sizeof(float) == 4, "Float bit indexing requires 32-bit float");',
2378
- '_Static_assert(sizeof(double) == 8, "Float bit indexing requires 64-bit double");',
2410
+ `${assertKeyword}(sizeof(float) == 4, "Float bit indexing requires 32-bit float");`,
2411
+ `${assertKeyword}(sizeof(double) == 8, "Float bit indexing requires 64-bit double");`,
2379
2412
  "",
2380
2413
  );
2381
2414
  }
@@ -5273,16 +5306,47 @@ export default class CodeGenerator implements IOrchestrator {
5273
5306
  }
5274
5307
 
5275
5308
  private generateParameter(ctx: Parser.ParameterContext): string {
5276
- const constMod = ctx.constModifier() ? "const " : "";
5277
5309
  const typeName = this.getTypeName(ctx.type());
5278
5310
  const name = ctx.IDENTIFIER().getText();
5279
- const dims = ctx.arrayDimension();
5280
5311
 
5281
- // Reject ALL C-style array parameters - require C-Next style (dimensions in type)
5282
- // C-style: u8 data[8], u8 data[4][4], u8 data[]
5283
- // C-Next: u8[8] data, u8[4][4] data, u8[] data
5312
+ // Validate: Reject C-style array parameters
5313
+ this._validateCStyleArrayParam(ctx, typeName, name);
5314
+
5315
+ // Validate: Reject unbounded array dimensions
5316
+ this._validateUnboundedArrayParam(ctx);
5317
+
5318
+ // Pre-compute CodeGenState-dependent values
5319
+ const isModified = this._isCurrentParameterModified(name);
5320
+ const isPassByValue = this._isPassByValueType(typeName, name);
5321
+
5322
+ // Build normalized input using adapter
5323
+ const input = ParameterInputAdapter.fromAST(ctx, {
5324
+ getTypeName: (t) => this.getTypeName(t),
5325
+ generateType: (t) => this.generateType(t),
5326
+ generateExpression: (e) => this.generateExpression(e),
5327
+ callbackTypes: CodeGenState.callbackTypes,
5328
+ isKnownStruct: (t) => this.isKnownStruct(t),
5329
+ typeMap: TYPE_MAP,
5330
+ isModified,
5331
+ isPassByValue,
5332
+ });
5333
+
5334
+ // Use shared builder with C/C++ mode
5335
+ return ParameterSignatureBuilder.build(input, CppModeHelper.refOrPtr());
5336
+ }
5337
+
5338
+ /**
5339
+ * Validate: Reject C-style array parameters
5340
+ * C-style: u8 data[8], u8 data[4][4], u8 data[]
5341
+ * C-Next: u8[8] data, u8[4][4] data, u8[] data
5342
+ */
5343
+ private _validateCStyleArrayParam(
5344
+ ctx: Parser.ParameterContext,
5345
+ typeName: string,
5346
+ name: string,
5347
+ ): void {
5348
+ const dims = ctx.arrayDimension();
5284
5349
  if (dims.length > 0) {
5285
- const baseType = typeName;
5286
5350
  const dimensions = dims
5287
5351
  .map((dim) => `[${dim.expression()?.getText() ?? ""}]`)
5288
5352
  .join("");
@@ -5290,73 +5354,28 @@ export default class CodeGenerator implements IOrchestrator {
5290
5354
  const col = ctx.start?.column ?? 0;
5291
5355
  throw new Error(
5292
5356
  `${line}:${col} C-style array parameter is not allowed. ` +
5293
- `Use '${baseType}${dimensions} ${name}' instead of '${baseType} ${name}${dimensions}'`,
5357
+ `Use '${typeName}${dimensions} ${name}' instead of '${typeName} ${name}${dimensions}'`,
5294
5358
  );
5295
5359
  }
5360
+ }
5296
5361
 
5297
- // ADR-029: Check if this is a callback type parameter
5298
- if (CodeGenState.callbackTypes.has(typeName)) {
5299
- const callbackInfo = CodeGenState.callbackTypes.get(typeName)!;
5300
- return `${callbackInfo.typedefName} ${name}`;
5301
- }
5302
-
5303
- const type = this.generateType(ctx.type());
5304
-
5305
- // Handle C-Next style array type in parameter (e.g., u8[8] param, u8[4][4] param, string<32>[5] param)
5362
+ /**
5363
+ * Validate: Reject unbounded array dimensions for memory safety
5364
+ */
5365
+ private _validateUnboundedArrayParam(ctx: Parser.ParameterContext): void {
5306
5366
  const arrayTypeCtx = ctx.type().arrayType();
5307
- if (arrayTypeCtx) {
5308
- // Check for unbounded dimensions - reject for memory safety
5309
- const allDims = arrayTypeCtx.arrayTypeDimension();
5310
- const hasUnboundedDim = allDims.some((d) => !d.expression());
5311
- if (hasUnboundedDim) {
5312
- const line = ctx.start?.line ?? 0;
5313
- const col = ctx.start?.column ?? 0;
5314
- throw new Error(
5315
- `${line}:${col} Unbounded array parameters are not allowed. ` +
5316
- `All dimensions must have explicit sizes for memory safety.`,
5317
- );
5318
- }
5319
-
5320
- let dims = allDims
5321
- .map((d) => {
5322
- const expr = d.expression();
5323
- return expr ? `[${this.generateExpression(expr)}]` : "[]";
5324
- })
5325
- .join("");
5326
- // For string arrays, add the string capacity dimension (string<32>[5] -> char[5][33])
5327
- if (arrayTypeCtx.stringType()) {
5328
- const stringCtx = arrayTypeCtx.stringType()!;
5329
- const intLiteral = stringCtx.INTEGER_LITERAL();
5330
- if (intLiteral) {
5331
- const capacity = Number.parseInt(intLiteral.getText(), 10);
5332
- dims += `[${capacity + 1}]`;
5333
- }
5334
- }
5335
- const wasModified = this._isCurrentParameterModified(name);
5336
- const autoConst = !wasModified && !constMod ? "const " : "";
5337
- return `${autoConst}${constMod}${type} ${name}${dims}`;
5338
- }
5367
+ if (!arrayTypeCtx) return;
5339
5368
 
5340
- // Pass-by-value types
5341
- if (this._isPassByValueType(typeName, name)) {
5342
- return `${constMod}${type} ${name}`;
5369
+ const allDims = arrayTypeCtx.arrayTypeDimension();
5370
+ const hasUnboundedDim = allDims.some((d) => !d.expression());
5371
+ if (hasUnboundedDim) {
5372
+ const line = ctx.start?.line ?? 0;
5373
+ const col = ctx.start?.column ?? 0;
5374
+ throw new Error(
5375
+ `${line}:${col} Unbounded array parameters are not allowed. ` +
5376
+ `All dimensions must have explicit sizes for memory safety.`,
5377
+ );
5343
5378
  }
5344
-
5345
- // Non-array string parameters
5346
- const stringResult = this._tryGenerateStringParam(
5347
- ctx,
5348
- constMod,
5349
- name,
5350
- dims,
5351
- );
5352
- if (stringResult) return stringResult;
5353
-
5354
- // Pass-by-reference types
5355
- const refResult = this._tryGenerateRefParam(constMod, type, typeName, name);
5356
- if (refResult) return refResult;
5357
-
5358
- // Unknown types use pass-by-value (standard C semantics)
5359
- return `${constMod}${type} ${name}`;
5360
5379
  }
5361
5380
 
5362
5381
  /**
@@ -5366,7 +5385,7 @@ export default class CodeGenerator implements IOrchestrator {
5366
5385
  // ISR, float, enum types
5367
5386
  if (typeName === "ISR") return true;
5368
5387
  if (this._isFloatType(typeName)) return true;
5369
- if (CodeGenState.symbols!.knownEnums.has(typeName)) return true;
5388
+ if (CodeGenState.symbols?.knownEnums.has(typeName)) return true;
5370
5389
 
5371
5390
  // Small unmodified primitives
5372
5391
  if (
@@ -5379,43 +5398,6 @@ export default class CodeGenerator implements IOrchestrator {
5379
5398
  return false;
5380
5399
  }
5381
5400
 
5382
- /**
5383
- * Try to generate non-array string parameter: string<N> -> char*
5384
- */
5385
- private _tryGenerateStringParam(
5386
- ctx: Parser.ParameterContext,
5387
- constMod: string,
5388
- name: string,
5389
- dims: Parser.ArrayDimensionContext[],
5390
- ): string | null {
5391
- if (!ctx.type().stringType() || dims.length !== 0) {
5392
- return null;
5393
- }
5394
-
5395
- const wasModified = this._isCurrentParameterModified(name);
5396
- const autoConst = !wasModified && !constMod ? "const " : "";
5397
- return `${autoConst}${constMod}char* ${name}`;
5398
- }
5399
-
5400
- /**
5401
- * Try to generate pass-by-reference parameter for known types
5402
- */
5403
- private _tryGenerateRefParam(
5404
- constMod: string,
5405
- type: string,
5406
- typeName: string,
5407
- name: string,
5408
- ): string | null {
5409
- if (!this.isKnownStruct(typeName) && !this._isKnownPrimitive(typeName)) {
5410
- return null;
5411
- }
5412
-
5413
- const wasModified = this._isCurrentParameterModified(name);
5414
- const autoConst = !wasModified && !constMod ? "const " : "";
5415
- const refOrPtr = CppModeHelper.refOrPtr();
5416
- return `${autoConst}${constMod}${type}${refOrPtr} ${name}`;
5417
- }
5418
-
5419
5401
  // ========================================================================
5420
5402
  // Variables
5421
5403
  // ========================================================================