c-next 0.1.25 → 0.1.26
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 +1 -1
- package/src/codegen/CodeGenerator.ts +16 -1
- package/src/codegen/HeaderGenerator.ts +3 -1
- package/src/codegen/generators/support/IncludeGenerator.ts +78 -2
- package/src/codegen/types/ICodeGeneratorOptions.ts +10 -0
- package/src/index.ts +26 -1
- package/src/pipeline/Pipeline.ts +2 -0
- package/src/project/Project.ts +1 -0
- package/src/project/types/IProjectConfig.ts +3 -0
package/package.json
CHANGED
|
@@ -255,6 +255,12 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
255
255
|
/** ADR-010: Source file path for validating includes */
|
|
256
256
|
private sourcePath: string | null = null;
|
|
257
257
|
|
|
258
|
+
/** Issue #349: Include directories for resolving angle-bracket .cnx includes */
|
|
259
|
+
private includeDirs: string[] = [];
|
|
260
|
+
|
|
261
|
+
/** Issue #349: Input directories for calculating relative paths */
|
|
262
|
+
private inputs: string[] = [];
|
|
263
|
+
|
|
258
264
|
/** Token stream for comment extraction (ADR-043) */
|
|
259
265
|
private tokenStream: CommonTokenStream | null = null;
|
|
260
266
|
|
|
@@ -1309,6 +1315,10 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
1309
1315
|
// ADR-010: Store source path for include validation
|
|
1310
1316
|
this.sourcePath = options?.sourcePath ?? null;
|
|
1311
1317
|
|
|
1318
|
+
// Issue #349: Store include directories and inputs for angle-bracket include resolution
|
|
1319
|
+
this.includeDirs = options?.includeDirs ?? [];
|
|
1320
|
+
this.inputs = options?.inputs ?? [];
|
|
1321
|
+
|
|
1312
1322
|
// Issue #250: Store C++ mode for temp variable generation
|
|
1313
1323
|
this.cppMode = options?.cppMode ?? false;
|
|
1314
1324
|
// Reset temp var state for each generation
|
|
@@ -1617,9 +1627,14 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
1617
1627
|
/**
|
|
1618
1628
|
* ADR-010: Transform #include directives, converting .cnx to .h
|
|
1619
1629
|
* ADR-053 A5: Delegates to IncludeGenerator
|
|
1630
|
+
* Issue #349: Now passes includeDirs and inputs for angle-bracket resolution
|
|
1620
1631
|
*/
|
|
1621
1632
|
private transformIncludeDirective(includeText: string): string {
|
|
1622
|
-
return includeTransformIncludeDirective(includeText,
|
|
1633
|
+
return includeTransformIncludeDirective(includeText, {
|
|
1634
|
+
sourcePath: this.sourcePath,
|
|
1635
|
+
includeDirs: this.includeDirs,
|
|
1636
|
+
inputs: this.inputs,
|
|
1637
|
+
});
|
|
1623
1638
|
}
|
|
1624
1639
|
|
|
1625
1640
|
// Issue #63: validateIncludeNotImplementationFile moved to TypeValidator
|
|
@@ -268,7 +268,9 @@ class HeaderGenerator {
|
|
|
268
268
|
passByValueParams?: TPassByValueParams,
|
|
269
269
|
): string | null {
|
|
270
270
|
// Map return type from C-Next to C
|
|
271
|
-
|
|
271
|
+
// Special case: main() always returns int for C/C++ compatibility
|
|
272
|
+
const mappedType = sym.type ? mapType(sym.type) : "void";
|
|
273
|
+
const returnType = sym.name === "main" ? "int" : mappedType;
|
|
272
274
|
|
|
273
275
|
// Issue #269: Get pass-by-value parameter names for this function
|
|
274
276
|
const passByValueSet = passByValueParams?.get(sym.name);
|
|
@@ -6,22 +6,98 @@ import * as fs from "fs";
|
|
|
6
6
|
import * as path from "path";
|
|
7
7
|
import * as Parser from "../../../parser/grammar/CNextParser";
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Issue #349: Options for include transformation
|
|
11
|
+
*/
|
|
12
|
+
interface IIncludeTransformOptions {
|
|
13
|
+
sourcePath: string | null;
|
|
14
|
+
includeDirs?: string[];
|
|
15
|
+
inputs?: string[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Issue #349: Find a .cnx file in the given search paths.
|
|
20
|
+
* Returns the absolute path if found, null otherwise.
|
|
21
|
+
*/
|
|
22
|
+
const findCnxFile = (
|
|
23
|
+
filename: string,
|
|
24
|
+
searchPaths: string[],
|
|
25
|
+
): string | null => {
|
|
26
|
+
for (const searchPath of searchPaths) {
|
|
27
|
+
const cnxPath = path.resolve(searchPath, `${filename}.cnx`);
|
|
28
|
+
if (fs.existsSync(cnxPath)) {
|
|
29
|
+
return cnxPath;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Issue #349: Calculate the relative path from input directories.
|
|
37
|
+
* Returns the relative path (e.g., "Display/utils.cnx") or null if not found.
|
|
38
|
+
*/
|
|
39
|
+
const getRelativePathFromInputs = (
|
|
40
|
+
filePath: string,
|
|
41
|
+
inputs: string[],
|
|
42
|
+
): string | null => {
|
|
43
|
+
for (const input of inputs) {
|
|
44
|
+
const resolvedInput = path.resolve(input);
|
|
45
|
+
|
|
46
|
+
// Skip if input is a file (not a directory)
|
|
47
|
+
if (fs.existsSync(resolvedInput) && fs.statSync(resolvedInput).isFile()) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const relativePath = path.relative(resolvedInput, filePath);
|
|
52
|
+
|
|
53
|
+
// If relative path doesn't start with '..' it's under this input
|
|
54
|
+
if (!relativePath.startsWith("..") && !path.isAbsolute(relativePath)) {
|
|
55
|
+
return relativePath;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
};
|
|
60
|
+
|
|
9
61
|
/**
|
|
10
62
|
* ADR-010: Transform #include directives, converting .cnx to .h
|
|
11
63
|
* Validates that .cnx files exist if sourcePath is available
|
|
12
64
|
* Supports both <file.cnx> and "file.cnx" forms
|
|
65
|
+
*
|
|
66
|
+
* Issue #349: For angle-bracket includes, resolves the correct output path
|
|
67
|
+
* by finding the .cnx file and calculating its relative path from inputs.
|
|
13
68
|
*/
|
|
14
69
|
const transformIncludeDirective = (
|
|
15
70
|
includeText: string,
|
|
16
|
-
|
|
71
|
+
options: IIncludeTransformOptions,
|
|
17
72
|
): string => {
|
|
73
|
+
const { sourcePath, includeDirs = [], inputs = [] } = options;
|
|
74
|
+
|
|
18
75
|
// Match: #include <file.cnx> or #include "file.cnx"
|
|
19
76
|
const angleMatch = includeText.match(/#\s*include\s*<([^>]+)\.cnx>/);
|
|
20
77
|
const quoteMatch = includeText.match(/#\s*include\s*"([^"]+)\.cnx"/);
|
|
21
78
|
|
|
22
79
|
if (angleMatch) {
|
|
23
80
|
const filename = angleMatch[1];
|
|
24
|
-
|
|
81
|
+
|
|
82
|
+
// Issue #349: Try to resolve the .cnx file to get correct output path
|
|
83
|
+
if (sourcePath) {
|
|
84
|
+
const sourceDir = path.dirname(sourcePath);
|
|
85
|
+
// Build search paths: source directory first, then include directories
|
|
86
|
+
const searchPaths = [sourceDir, ...includeDirs];
|
|
87
|
+
|
|
88
|
+
const foundPath = findCnxFile(filename, searchPaths);
|
|
89
|
+
if (foundPath && inputs.length > 0) {
|
|
90
|
+
// Calculate relative path from inputs for correct header path
|
|
91
|
+
const relativePath = getRelativePathFromInputs(foundPath, inputs);
|
|
92
|
+
if (relativePath) {
|
|
93
|
+
// Transform .cnx to .h
|
|
94
|
+
const headerPath = relativePath.replace(/\.cnx$/, ".h");
|
|
95
|
+
return includeText.replace(`<${filename}.cnx>`, `<${headerPath}>`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Fallback: simple replacement (for external includes or when resolution fails)
|
|
25
101
|
return includeText.replace(`<${filename}.cnx>`, `<${filename}.h>`);
|
|
26
102
|
} else if (quoteMatch) {
|
|
27
103
|
const filepath = quoteMatch[1];
|
|
@@ -24,6 +24,16 @@ interface ICodeGeneratorOptions {
|
|
|
24
24
|
* Example: "Display/Utils.cnx" -> #include "Display/Utils.h"
|
|
25
25
|
*/
|
|
26
26
|
sourceRelativePath?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Issue #349: Include directories for resolving angle-bracket .cnx includes.
|
|
29
|
+
* Used to search for .cnx files referenced in #include <file.cnx> directives.
|
|
30
|
+
*/
|
|
31
|
+
includeDirs?: string[];
|
|
32
|
+
/**
|
|
33
|
+
* Issue #349: Input directories for calculating relative paths.
|
|
34
|
+
* Used to determine the correct output path prefix for headers.
|
|
35
|
+
*/
|
|
36
|
+
inputs?: string[];
|
|
27
37
|
}
|
|
28
38
|
|
|
29
39
|
export default ICodeGeneratorOptions;
|
package/src/index.ts
CHANGED
|
@@ -196,6 +196,7 @@ async function runUnifiedMode(
|
|
|
196
196
|
verbose: boolean,
|
|
197
197
|
cppRequired: boolean,
|
|
198
198
|
noCache: boolean,
|
|
199
|
+
parseOnly: boolean,
|
|
199
200
|
headerOutDir?: string,
|
|
200
201
|
): Promise<void> {
|
|
201
202
|
// Issue #337: Identify which inputs are directories (for structure preservation)
|
|
@@ -283,6 +284,7 @@ async function runUnifiedMode(
|
|
|
283
284
|
defines,
|
|
284
285
|
cppRequired,
|
|
285
286
|
noCache,
|
|
287
|
+
parseOnly,
|
|
286
288
|
});
|
|
287
289
|
|
|
288
290
|
// Step 5: Compile
|
|
@@ -494,6 +496,7 @@ async function main(): Promise<void> {
|
|
|
494
496
|
let preprocess = true;
|
|
495
497
|
let verbose = false;
|
|
496
498
|
let noCache = false;
|
|
499
|
+
let parseOnly = false;
|
|
497
500
|
let headerOutDir: string | undefined;
|
|
498
501
|
let cleanMode = false;
|
|
499
502
|
|
|
@@ -514,6 +517,8 @@ async function main(): Promise<void> {
|
|
|
514
517
|
preprocess = false;
|
|
515
518
|
} else if (arg === "--no-cache") {
|
|
516
519
|
noCache = true;
|
|
520
|
+
} else if (arg === "--parse") {
|
|
521
|
+
parseOnly = true;
|
|
517
522
|
} else if (arg === "--header-out" && i + 1 < args.length) {
|
|
518
523
|
headerOutDir = args[++i];
|
|
519
524
|
} else if (arg === "--clean") {
|
|
@@ -526,7 +531,26 @@ async function main(): Promise<void> {
|
|
|
526
531
|
} else {
|
|
527
532
|
defines[define] = true;
|
|
528
533
|
}
|
|
529
|
-
} else if (
|
|
534
|
+
} else if (arg.startsWith("-I")) {
|
|
535
|
+
// Common mistake: -I is GCC syntax, we use --include
|
|
536
|
+
// Catches both "-I path" and "-Ipath" (no space)
|
|
537
|
+
console.error(`Error: Unknown flag '${arg}'`);
|
|
538
|
+
console.error(" Did you mean: --include <dir>");
|
|
539
|
+
console.error("");
|
|
540
|
+
console.error("Example:");
|
|
541
|
+
console.error(" cnext src --include path/to/headers");
|
|
542
|
+
process.exit(1);
|
|
543
|
+
} else if (arg.startsWith("--")) {
|
|
544
|
+
// Catch unknown long flags (e.g., --foo)
|
|
545
|
+
console.error(`Error: Unknown flag '${arg}'`);
|
|
546
|
+
showHelp();
|
|
547
|
+
process.exit(1);
|
|
548
|
+
} else if (arg.startsWith("-")) {
|
|
549
|
+
// Catch other unknown short flags (e.g., -x)
|
|
550
|
+
console.error(`Error: Unknown flag '${arg}'`);
|
|
551
|
+
showHelp();
|
|
552
|
+
process.exit(1);
|
|
553
|
+
} else {
|
|
530
554
|
inputFiles.push(arg);
|
|
531
555
|
}
|
|
532
556
|
}
|
|
@@ -563,6 +587,7 @@ async function main(): Promise<void> {
|
|
|
563
587
|
verbose,
|
|
564
588
|
cppRequired,
|
|
565
589
|
noCache,
|
|
590
|
+
parseOnly,
|
|
566
591
|
headerOutDir,
|
|
567
592
|
);
|
|
568
593
|
}
|
package/src/pipeline/Pipeline.ts
CHANGED
|
@@ -643,6 +643,8 @@ class Pipeline {
|
|
|
643
643
|
generateHeaders: this.config.generateHeaders, // Issue #230: Enable self-include when headers are generated
|
|
644
644
|
cppMode: this.cppDetected, // Issue #250: C++ compatible code generation
|
|
645
645
|
sourceRelativePath: this.getSourceRelativePath(file.path), // Issue #339: For correct self-include paths
|
|
646
|
+
includeDirs: this.config.includeDirs, // Issue #349: For angle-bracket include resolution
|
|
647
|
+
inputs: this.config.inputs, // Issue #349: For calculating relative paths
|
|
646
648
|
},
|
|
647
649
|
);
|
|
648
650
|
|
package/src/project/Project.ts
CHANGED
|
@@ -37,6 +37,9 @@ interface IProjectConfig {
|
|
|
37
37
|
|
|
38
38
|
/** Issue #183: Disable symbol caching (default: false = cache enabled) */
|
|
39
39
|
noCache?: boolean;
|
|
40
|
+
|
|
41
|
+
/** Parse only mode - validate syntax without generating output */
|
|
42
|
+
parseOnly?: boolean;
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
export default IProjectConfig;
|