c-next 0.1.7 → 0.1.8

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.7",
3
+ "version": "0.1.8",
4
4
  "description": "A safer C for embedded systems development. Transpiles to clean, readable C.",
5
5
  "main": "src/index.ts",
6
6
  "bin": {
@@ -5592,10 +5592,12 @@ export default class CodeGenerator implements IOrchestrator {
5592
5592
 
5593
5593
  // ADR-040: ISR arrays use normal array indexing, not bit manipulation
5594
5594
  // Also handle any array type that isn't an integer scalar
5595
+ // Issue #213: String parameters (isString=true) should also use memcpy for slice assignment
5595
5596
  const isActualArray =
5596
- typeInfo?.isArray &&
5597
- typeInfo.arrayDimensions &&
5598
- typeInfo.arrayDimensions.length > 0;
5597
+ (typeInfo?.isArray &&
5598
+ typeInfo.arrayDimensions &&
5599
+ typeInfo.arrayDimensions.length > 0) ||
5600
+ typeInfo?.isString;
5599
5601
  const isISRType = typeInfo?.baseType === "ISR";
5600
5602
 
5601
5603
  if (isActualArray || isISRType) {
@@ -5617,6 +5619,16 @@ export default class CodeGenerator implements IOrchestrator {
5617
5619
 
5618
5620
  // Generate bounds-checked memcpy
5619
5621
  // if (offset + length <= sizeof(buffer)) { memcpy(&buffer[offset], &value, length); }
5622
+ // Issue #213: For string parameters, use capacity for bounds checking
5623
+ // since sizeof(char*) gives pointer size, not buffer size
5624
+ if (
5625
+ typeInfo?.isString &&
5626
+ typeInfo.stringCapacity &&
5627
+ !typeInfo.isArray
5628
+ ) {
5629
+ const capacity = typeInfo.stringCapacity + 1;
5630
+ return `if (${offset} + ${length} <= ${capacity}) { memcpy(&${name}[${offset}], &${value}, ${length}); }`;
5631
+ }
5620
5632
  return `if (${offset} + ${length} <= sizeof(${name})) { memcpy(&${name}[${offset}], &${value}, ${length}); }`;
5621
5633
  }
5622
5634
 
@@ -6693,6 +6705,33 @@ export default class CodeGenerator implements IOrchestrator {
6693
6705
  continue; // Skip further processing, this just sets the base identifier
6694
6706
  }
6695
6707
 
6708
+ // Issue #212: Check if 'length' is a scope variable before treating it as a property accessor
6709
+ // When accessing this.length, we need to check if 'length' is a scope variable name
6710
+ // If so, transform it to the scope variable (e.g., Scope_length) instead of treating
6711
+ // it as the .length property accessor
6712
+ if (result === "__THIS_SCOPE__" && memberName === "length") {
6713
+ if (!this.context.currentScope) {
6714
+ throw new Error("Error: 'this' can only be used inside a scope");
6715
+ }
6716
+ const members = this.context.scopeMembers.get(
6717
+ this.context.currentScope,
6718
+ );
6719
+ if (members && members.has("length")) {
6720
+ // This is a scope variable named 'length', not a property accessor
6721
+ result = `${this.context.currentScope}_${memberName}`;
6722
+ currentIdentifier = result;
6723
+ // Set struct type for chained access if applicable
6724
+ const resolvedTypeInfo = this.context.typeRegistry.get(result);
6725
+ if (
6726
+ resolvedTypeInfo &&
6727
+ this.isKnownStruct(resolvedTypeInfo.baseType)
6728
+ ) {
6729
+ currentStructType = resolvedTypeInfo.baseType;
6730
+ }
6731
+ continue; // Skip the .length property handler
6732
+ }
6733
+ }
6734
+
6696
6735
  // Handle .length property for arrays, strings, and integers
6697
6736
  if (memberName === "length") {
6698
6737
  // Special case: main function's args.length -> argc
package/src/index.ts CHANGED
@@ -25,7 +25,8 @@ const packageJson = require("../package.json");
25
25
  * C-Next configuration file options
26
26
  */
27
27
  interface ICNextConfig {
28
- outputExtension?: ".c" | ".cpp";
28
+ /** Issue #211: Force C++ output. Auto-detection may also enable this. */
29
+ cppRequired?: boolean;
29
30
  generateHeaders?: boolean;
30
31
  debugMode?: boolean;
31
32
  target?: string; // ADR-049: Target platform (e.g., "teensy41", "cortex-m0")
@@ -130,7 +131,7 @@ function showHelp(): void {
130
131
  console.log(" cnext.config.json, .cnext.json, .cnextrc");
131
132
  console.log("");
132
133
  console.log("Config example:");
133
- console.log(' { "outputExtension": ".cpp" }');
134
+ console.log(' { "cppRequired": true }');
134
135
  console.log("");
135
136
  console.log("A safer C for embedded systems development.");
136
137
  }
@@ -188,7 +189,7 @@ async function runUnifiedMode(
188
189
  generateHeaders: boolean,
189
190
  preprocess: boolean,
190
191
  verbose: boolean,
191
- outputExtension: ".c" | ".cpp",
192
+ cppRequired: boolean,
192
193
  noCache: boolean,
193
194
  ): Promise<void> {
194
195
  // Step 1: Expand directories to .cnx files
@@ -257,7 +258,7 @@ async function runUnifiedMode(
257
258
  generateHeaders,
258
259
  preprocess,
259
260
  defines,
260
- outputExtension,
261
+ cppRequired,
261
262
  noCache,
262
263
  });
263
264
 
@@ -466,7 +467,7 @@ async function main(): Promise<void> {
466
467
  const includeDirs: string[] = [];
467
468
  const defines: Record<string, string | boolean> = {};
468
469
  let cliGenerateHeaders: boolean | undefined;
469
- let cliOutputExtension: ".c" | ".cpp" | undefined;
470
+ let cliCppRequired: boolean | undefined;
470
471
  let preprocess = true;
471
472
  let verbose = false;
472
473
  let noCache = false;
@@ -483,7 +484,7 @@ async function main(): Promise<void> {
483
484
  } else if (arg === "--exclude-headers") {
484
485
  cliGenerateHeaders = false;
485
486
  } else if (arg === "--cpp") {
486
- cliOutputExtension = ".cpp";
487
+ cliCppRequired = true;
487
488
  } else if (arg === "--no-preprocess") {
488
489
  preprocess = false;
489
490
  } else if (arg === "--no-cache") {
@@ -508,7 +509,7 @@ async function main(): Promise<void> {
508
509
 
509
510
  // Apply config defaults, CLI flags take precedence
510
511
  const generateHeaders = cliGenerateHeaders ?? config.generateHeaders ?? true;
511
- const outputExtension = cliOutputExtension ?? config.outputExtension ?? ".c";
512
+ const cppRequired = cliCppRequired ?? config.cppRequired ?? false;
512
513
 
513
514
  // Unified mode - always use Project class with header discovery
514
515
  if (inputFiles.length === 0) {
@@ -525,7 +526,7 @@ async function main(): Promise<void> {
525
526
  generateHeaders,
526
527
  preprocess,
527
528
  verbose,
528
- outputExtension,
529
+ cppRequired,
529
530
  noCache,
530
531
  );
531
532
  }
@@ -56,6 +56,8 @@ class Pipeline {
56
56
  private headerGenerator: HeaderGenerator;
57
57
  private warnings: string[];
58
58
  private cacheManager: CacheManager | null;
59
+ /** Issue #211: Tracks if C++ output is needed (one-way flag, false → true only) */
60
+ private cppDetected: boolean;
59
61
 
60
62
  constructor(config: IPipelineConfig) {
61
63
  // Apply defaults
@@ -66,7 +68,7 @@ class Pipeline {
66
68
  defines: config.defines ?? {},
67
69
  preprocess: config.preprocess ?? true,
68
70
  generateHeaders: config.generateHeaders ?? true,
69
- outputExtension: config.outputExtension ?? ".c",
71
+ cppRequired: config.cppRequired ?? false,
70
72
  parseOnly: config.parseOnly ?? false,
71
73
  debugMode: config.debugMode ?? false,
72
74
  target: config.target ?? "",
@@ -74,6 +76,9 @@ class Pipeline {
74
76
  noCache: config.noCache ?? false,
75
77
  };
76
78
 
79
+ // Issue #211: Initialize cppDetected from config (--cpp flag sets this)
80
+ this.cppDetected = this.config.cppRequired;
81
+
77
82
  this.symbolTable = new SymbolTable();
78
83
  this.preprocessor = new Preprocessor();
79
84
  this.codeGenerator = new CodeGenerator();
@@ -317,7 +322,20 @@ class Pipeline {
317
322
  this.symbolTable.restoreStructFields(cached.structFields);
318
323
  this.symbolTable.restoreNeedsStructKeyword(cached.needsStructKeyword);
319
324
  this.symbolTable.restoreEnumBitWidths(cached.enumBitWidth);
320
- return; // Cache hit - skip parsing
325
+
326
+ // Issue #211: Still check for C++ syntax even on cache hit
327
+ // The detection is cheap (regex only) and ensures cppDetected is set correctly
328
+ if (file.type === EFileType.CHeader) {
329
+ const content = readFileSync(file.path, "utf-8");
330
+ if (detectCppSyntax(content)) {
331
+ this.cppDetected = true;
332
+ }
333
+ } else if (file.type === EFileType.CppHeader) {
334
+ // .hpp files are always C++
335
+ this.cppDetected = true;
336
+ }
337
+
338
+ return; // Cache hit - skip full parsing
321
339
  }
322
340
  }
323
341
 
@@ -347,6 +365,8 @@ class Pipeline {
347
365
  if (file.type === EFileType.CHeader) {
348
366
  this.parseCHeader(content, file.path);
349
367
  } else if (file.type === EFileType.CppHeader) {
368
+ // Issue #211: .hpp files are always C++
369
+ this.cppDetected = true;
350
370
  this.parseCppHeader(content, file.path);
351
371
  }
352
372
 
@@ -374,6 +394,8 @@ class Pipeline {
374
394
  */
375
395
  private parseCHeader(content: string, filePath: string): void {
376
396
  if (detectCppSyntax(content)) {
397
+ // Issue #211: C++ detected, set flag for .cpp output
398
+ this.cppDetected = true;
377
399
  // Use C++14 parser for headers with C++ syntax (typed enums, classes, etc.)
378
400
  this.parseCppHeader(content, filePath);
379
401
  } else {
@@ -580,7 +602,8 @@ class Pipeline {
580
602
  * Get output path for a file
581
603
  */
582
604
  private getOutputPath(file: IDiscoveredFile): string {
583
- const ext = this.config.outputExtension;
605
+ // Issue #211: Derive extension from cppDetected flag
606
+ const ext = this.cppDetected ? ".cpp" : ".c";
584
607
  const outputName = basename(file.path).replace(/\.cnx$|\.cnext$/, ext);
585
608
 
586
609
  // Check if file is in any input directory (for preserving structure)
@@ -23,8 +23,8 @@ interface IPipelineConfig {
23
23
  /** Whether to generate .h files for exported symbols (default: true) */
24
24
  generateHeaders?: boolean;
25
25
 
26
- /** Output file extension (default: ".c") */
27
- outputExtension?: ".c" | ".cpp";
26
+ /** Issue #211: Force C++ output (--cpp flag). Auto-detection may also enable this. */
27
+ cppRequired?: boolean;
28
28
 
29
29
  /** Parse only mode - no code generation */
30
30
  parseOnly?: boolean;
@@ -49,7 +49,7 @@ class Project {
49
49
  defines: this.config.defines,
50
50
  preprocess: this.config.preprocess,
51
51
  generateHeaders: this.config.generateHeaders,
52
- outputExtension: this.config.outputExtension,
52
+ cppRequired: this.config.cppRequired,
53
53
  noCache: this.config.noCache,
54
54
  });
55
55
  }
@@ -29,8 +29,8 @@ interface IProjectConfig {
29
29
  /** Additional preprocessor defines */
30
30
  defines?: Record<string, string | boolean>;
31
31
 
32
- /** Output file extension (default: ".c") */
33
- outputExtension?: ".c" | ".cpp";
32
+ /** Issue #211: Force C++ output. Auto-detection may also enable this. */
33
+ cppRequired?: boolean;
34
34
 
35
35
  /** Issue #183: Disable symbol caching (default: false = cache enabled) */
36
36
  noCache?: boolean;