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
|
@@ -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
|
-
|
|
5598
|
-
|
|
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
|
-
|
|
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(' { "
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
529
|
+
cppRequired,
|
|
529
530
|
noCache,
|
|
530
531
|
);
|
|
531
532
|
}
|
package/src/pipeline/Pipeline.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
/**
|
|
27
|
-
|
|
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;
|
package/src/project/Project.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
/**
|
|
33
|
-
|
|
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;
|