c-next 0.1.58 → 0.1.60
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/README.md +2 -3
- package/package.json +13 -4
- package/src/cli/ArgParser.ts +12 -0
- package/src/cli/CleanCommand.ts +56 -44
- package/src/cli/Cli.ts +10 -0
- package/src/cli/PlatformIOCommand.ts +27 -18
- package/src/cli/Runner.ts +115 -69
- package/src/cli/__tests__/ArgParser.test.ts +16 -12
- package/src/cli/__tests__/Cli.test.ts +13 -0
- package/src/cli/serve/JsonRpcHandler.ts +155 -0
- package/src/cli/serve/ServeCommand.ts +236 -0
- package/src/cli/serve/__tests__/JsonRpcHandler.test.ts +199 -0
- package/src/cli/serve/__tests__/ServeCommand.test.ts +224 -0
- package/src/cli/serve/types/IJsonRpcRequest.ts +13 -0
- package/src/cli/serve/types/IJsonRpcResponse.ts +18 -0
- package/src/cli/types/ICliResult.ts +4 -0
- package/src/cli/types/IParsedArgs.ts +2 -0
- package/src/index.ts +7 -0
- package/src/lib/__tests__/transpiler.test.ts +60 -0
- package/src/lib/parseWithSymbols.ts +8 -1
- package/src/lib/transpiler.ts +19 -154
- package/src/lib/types/TSymbolKind.ts +4 -0
- package/src/transpiler/Transpiler.ts +469 -310
- package/src/transpiler/__tests__/DualCodePaths.test.ts +110 -0
- package/src/transpiler/__tests__/Transpiler.coverage.test.ts +1 -1
- package/src/transpiler/__tests__/Transpiler.test.ts +34 -0
- package/src/transpiler/data/DependencyGraph.ts +1 -1
- package/src/transpiler/data/IncludeDiscovery.ts +42 -35
- package/src/transpiler/data/IncludeResolver.ts +157 -88
- package/src/transpiler/logic/analysis/FloatModuloAnalyzer.ts +15 -16
- package/src/transpiler/logic/analysis/GrammarCoverageListener.ts +7 -34
- package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +120 -88
- package/src/transpiler/logic/analysis/__tests__/CommentExtractor.test.ts +54 -0
- package/src/transpiler/logic/analysis/__tests__/DivisionByZeroAnalyzer.test.ts +49 -0
- package/src/transpiler/logic/analysis/__tests__/FloatModuloAnalyzer.test.ts +64 -0
- package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +49 -0
- package/src/transpiler/logic/analysis/__tests__/GrammarCoverageListener.test.ts +281 -0
- package/src/transpiler/logic/analysis/__tests__/InitializationAnalyzer.test.ts +618 -0
- package/src/transpiler/logic/analysis/__tests__/NullCheckAnalyzer.test.ts +131 -0
- package/src/transpiler/logic/analysis/__tests__/ScopeStack.test.ts +15 -0
- package/src/transpiler/logic/analysis/__tests__/StructFieldAnalyzer.test.ts +159 -0
- package/src/transpiler/logic/analysis/__tests__/runAnalyzers.test.ts +304 -0
- package/src/transpiler/logic/analysis/runAnalyzers.ts +66 -105
- package/src/transpiler/logic/analysis/types/GrammarCoverageReportBuilder.ts +61 -0
- package/src/transpiler/logic/symbols/CSymbolCollector.ts +104 -61
- package/src/transpiler/logic/symbols/CppSymbolCollector.ts +240 -136
- package/src/transpiler/logic/symbols/__tests__/CppSymbolCollector.test.ts +65 -0
- package/src/transpiler/logic/symbols/__tests__/TransitiveEnumCollector.test.ts +129 -0
- package/src/transpiler/logic/symbols/cnext/__tests__/StructCollector.test.ts +22 -0
- package/src/transpiler/logic/symbols/cnext/__tests__/TSymbolAdapter.test.ts +16 -1
- package/src/transpiler/logic/symbols/cnext/adapters/TSymbolAdapter.ts +32 -7
- package/src/transpiler/logic/symbols/cnext/adapters/TSymbolInfoAdapter.ts +54 -31
- package/src/transpiler/logic/symbols/cnext/collectors/BitmapCollector.ts +1 -0
- package/src/transpiler/logic/symbols/cnext/collectors/EnumCollector.ts +1 -0
- package/src/transpiler/logic/symbols/cnext/collectors/FunctionCollector.ts +1 -0
- package/src/transpiler/logic/symbols/cnext/collectors/RegisterCollector.ts +1 -0
- package/src/transpiler/logic/symbols/cnext/collectors/StructCollector.ts +33 -24
- package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +69 -37
- package/src/transpiler/logic/symbols/cnext/index.ts +154 -98
- package/src/transpiler/logic/symbols/types/IBaseSymbol.ts +3 -0
- package/src/transpiler/output/codegen/CodeGenerator.ts +1790 -1877
- package/src/transpiler/output/codegen/TypeResolver.ts +3 -51
- package/src/transpiler/output/codegen/TypeValidator.ts +201 -188
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +6818 -0
- package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +41 -89
- package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +164 -0
- package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +429 -242
- package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +271 -0
- package/src/transpiler/output/codegen/assignment/handlers/AccessPatternHandlers.ts +12 -2
- package/src/transpiler/output/codegen/assignment/handlers/AssignmentHandlerUtils.ts +124 -0
- package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +11 -4
- package/src/transpiler/output/codegen/assignment/handlers/IRegisterNameResult.ts +14 -0
- package/src/transpiler/output/codegen/assignment/handlers/RegisterHandlers.ts +81 -90
- package/src/transpiler/output/codegen/assignment/handlers/RegisterUtils.ts +26 -0
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/AssignmentHandlerRegistry.test.ts +81 -0
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/AssignmentHandlerUtils.test.ts +164 -0
- package/src/transpiler/output/codegen/assignment/handlers/index.ts +12 -26
- package/src/transpiler/output/codegen/assignment/index.ts +10 -10
- package/src/transpiler/output/codegen/generators/IOrchestrator.ts +2 -4
- package/src/transpiler/output/codegen/generators/declarationGenerators/BitmapCommentUtils.ts +61 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/BitmapGenerator.ts +3 -12
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +327 -242
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/BitmapCommentUtils.test.ts +52 -0
- package/src/transpiler/output/codegen/generators/expressions/AccessExprGenerator.ts +202 -156
- package/src/transpiler/output/codegen/generators/expressions/BinaryExprGenerator.ts +60 -45
- package/src/transpiler/output/codegen/generators/expressions/BitmapAccessHelper.ts +57 -0
- package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +115 -105
- package/src/transpiler/output/codegen/generators/expressions/CallExprUtils.ts +42 -0
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +1097 -591
- package/src/transpiler/output/codegen/generators/expressions/__tests__/BitmapAccessHelper.test.ts +115 -0
- package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +1257 -0
- package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprUtils.test.ts +145 -1
- package/src/transpiler/output/codegen/generators/expressions/__tests__/LiteralGenerator.test.ts +168 -0
- package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +146 -11
- package/src/transpiler/output/codegen/generators/statements/AtomicGenerator.ts +34 -35
- package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +47 -151
- package/src/transpiler/output/codegen/generators/statements/SwitchGenerator.ts +100 -72
- package/src/transpiler/output/codegen/generators/support/HelperGenerator.ts +5 -271
- package/src/transpiler/output/codegen/generators/support/OverflowHelperTemplates.ts +409 -0
- package/src/transpiler/output/codegen/generators/support/__tests__/OverflowHelperTemplates.test.ts +182 -0
- package/src/transpiler/output/codegen/helpers/ArrayDimensionParser.ts +167 -87
- package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +95 -61
- package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +16 -6
- package/src/transpiler/output/codegen/helpers/BitRangeHelper.ts +89 -0
- package/src/transpiler/output/codegen/helpers/BooleanHelper.ts +40 -0
- package/src/transpiler/output/codegen/helpers/CodeGenErrors.ts +77 -0
- package/src/transpiler/output/codegen/helpers/CppConstructorHelper.ts +75 -0
- package/src/transpiler/output/codegen/helpers/CppMemberHelper.ts +185 -0
- package/src/transpiler/output/codegen/helpers/IntegerLiteralValidator.ts +93 -0
- package/src/transpiler/output/codegen/helpers/MemberAccessValidator.ts +87 -0
- package/src/transpiler/output/codegen/helpers/SetMapHelper.ts +63 -0
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +205 -150
- package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +131 -0
- package/src/transpiler/output/codegen/helpers/VariableModifierBuilder.ts +108 -0
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +34 -0
- package/src/transpiler/output/codegen/helpers/__tests__/BitRangeHelper.test.ts +157 -0
- package/src/transpiler/output/codegen/helpers/__tests__/BooleanHelper.test.ts +68 -0
- package/src/transpiler/output/codegen/helpers/__tests__/CodeGenErrors.test.ts +132 -0
- package/src/transpiler/output/codegen/helpers/__tests__/CppConstructorHelper.test.ts +139 -0
- package/src/transpiler/output/codegen/helpers/__tests__/CppMemberHelper.test.ts +377 -0
- package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +31 -0
- package/src/transpiler/output/codegen/helpers/__tests__/IntegerLiteralValidator.test.ts +170 -0
- package/src/transpiler/output/codegen/helpers/__tests__/MemberAccessValidator.test.ts +185 -0
- package/src/transpiler/output/codegen/helpers/__tests__/SetMapHelper.test.ts +140 -0
- package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +326 -0
- package/src/transpiler/output/codegen/helpers/__tests__/VariableModifierBuilder.test.ts +164 -0
- package/src/transpiler/output/codegen/helpers/types/IPostfixOp.ts +12 -0
- package/src/transpiler/output/codegen/memberAccessChain.ts +242 -115
- package/src/transpiler/output/codegen/utils/ExpressionUnwrapper.ts +166 -0
- package/src/transpiler/output/codegen/utils/__tests__/ExpressionUnwrapper.test.ts +182 -0
- package/src/transpiler/output/headers/BaseHeaderGenerator.ts +225 -0
- package/src/transpiler/output/headers/CHeaderGenerator.ts +5 -196
- package/src/transpiler/output/headers/CppHeaderGenerator.ts +5 -196
- package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +21 -50
- package/src/transpiler/output/headers/__tests__/BaseHeaderGenerator.test.ts +245 -0
- package/src/transpiler/output/headers/generators/__tests__/generateStructHeader.test.ts +41 -0
- package/src/transpiler/output/headers/generators/generateStructHeader.ts +9 -1
- package/src/utils/ExpressionUtils.ts +17 -3
- package/src/utils/ParserUtils.ts +40 -0
- package/src/utils/__tests__/ParserUtils.test.ts +80 -0
package/README.md
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
# C-Next
|
|
2
2
|
|
|
3
|
-
[](https://github.com/jlaustill/c-next/actions/workflows/pr-checks.yml)
|
|
4
3
|
[](https://www.npmjs.com/package/c-next)
|
|
5
|
-
[](LICENSE)
|
|
6
5
|
[](https://sonarcloud.io/summary/new_code?id=jlaustill_c-next)
|
|
7
|
-
[](https://sonarcloud.io/summary/
|
|
6
|
+
[](https://sonarcloud.io/summary/overall?id=jlaustill_c-next)
|
|
8
7
|
[](https://jlaustill.github.io/c-next/coverage/)
|
|
9
8
|
[](https://sonarcloud.io/summary/new_code?id=jlaustill_c-next)
|
|
10
9
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "c-next",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.60",
|
|
4
4
|
"description": "A safer C for embedded systems development. Transpiles to clean, readable C.",
|
|
5
|
+
"packageManager": "npm@11.9.0",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"main": "src/index.ts",
|
|
7
8
|
"bin": {
|
|
@@ -48,7 +49,13 @@
|
|
|
48
49
|
"duplication": "npx jscpd src/ scripts/ --reporters console",
|
|
49
50
|
"duplication:json": "npx jscpd src/ scripts/ --reporters json --output .jscpd",
|
|
50
51
|
"duplication:sonar": "curl -s 'https://sonarcloud.io/api/measures/component_tree?component=jlaustill_c-next&metricKeys=duplicated_blocks,duplicated_lines&s=metric&metricSort=duplicated_blocks&metricSortFilter=withMeasuresOnly&asc=false&ps=20' | jq -r '.components[] | \"\\(.path)\\t\\(.measures[0].value) blocks\\t\\(.measures[1].value) lines\"'",
|
|
51
|
-
"duplication:all": "echo '=== Local (jscpd) ===' && npm run duplication && echo '\n=== SonarCloud ===' && npm run duplication:sonar"
|
|
52
|
+
"duplication:all": "echo '=== Local (jscpd) ===' && npm run duplication && echo '\n=== SonarCloud ===' && npm run duplication:sonar",
|
|
53
|
+
"analyze:prune": "ts-prune",
|
|
54
|
+
"analyze:madge": "madge src/ --circular --extensions ts",
|
|
55
|
+
"analyze:madge:graph": "madge src/ --image docs/architecture/dependency-graph.svg --extensions ts --exclude '(parser/(grammar|c/grammar|cpp/grammar)|__tests__)' --layout sfdp",
|
|
56
|
+
"analyze:cpd": "./scripts/cpd.sh",
|
|
57
|
+
"analyze:duplication": "npx jscpd src/ scripts/",
|
|
58
|
+
"analyze:all": "npm run analyze:duplication && npm run analyze:prune && npm run analyze:madge"
|
|
52
59
|
},
|
|
53
60
|
"keywords": [
|
|
54
61
|
"c",
|
|
@@ -78,7 +85,7 @@
|
|
|
78
85
|
"LICENSE"
|
|
79
86
|
],
|
|
80
87
|
"devDependencies": {
|
|
81
|
-
"@types/node": "^25.2.
|
|
88
|
+
"@types/node": "^25.2.1",
|
|
82
89
|
"@types/yargs": "^17.0.35",
|
|
83
90
|
"@vitest/coverage-v8": "^4.0.18",
|
|
84
91
|
"antlr4ng-cli": "^2.0.0",
|
|
@@ -86,10 +93,12 @@
|
|
|
86
93
|
"cspell": "^9.6.4",
|
|
87
94
|
"dependency-cruiser": "^17.3.7",
|
|
88
95
|
"husky": "^9.1.7",
|
|
89
|
-
"knip": "^5.83.
|
|
96
|
+
"knip": "^5.83.1",
|
|
90
97
|
"lint-staged": "^16.2.7",
|
|
98
|
+
"madge": "^8.0.0",
|
|
91
99
|
"oxlint": "^1.43.0",
|
|
92
100
|
"prettier": "^3.8.1",
|
|
101
|
+
"ts-prune": "^0.10.3",
|
|
93
102
|
"vitest": "^4.0.18"
|
|
94
103
|
},
|
|
95
104
|
"dependencies": {
|
package/src/cli/ArgParser.ts
CHANGED
|
@@ -30,6 +30,7 @@ interface IYargsResult {
|
|
|
30
30
|
cache: boolean;
|
|
31
31
|
"pio-install": boolean;
|
|
32
32
|
"pio-uninstall": boolean;
|
|
33
|
+
serve: boolean;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
/**
|
|
@@ -145,6 +146,13 @@ A safer C for embedded systems development.`,
|
|
|
145
146
|
default: false,
|
|
146
147
|
})
|
|
147
148
|
|
|
149
|
+
// Server mode
|
|
150
|
+
.option("serve", {
|
|
151
|
+
type: "boolean",
|
|
152
|
+
describe: "Start JSON-RPC server on stdin/stdout",
|
|
153
|
+
default: false,
|
|
154
|
+
})
|
|
155
|
+
|
|
148
156
|
// Config file documentation (shown in help)
|
|
149
157
|
.epilogue(
|
|
150
158
|
`Examples:
|
|
@@ -180,6 +188,9 @@ Config options:
|
|
|
180
188
|
.help("help")
|
|
181
189
|
.alias("help", "h")
|
|
182
190
|
|
|
191
|
+
// Strict mode - reject unknown options (but allow positional args)
|
|
192
|
+
.strictOptions()
|
|
193
|
+
|
|
183
194
|
// Fail handler for unknown options
|
|
184
195
|
.fail((msg, err, yargsInstance) => {
|
|
185
196
|
if (err) throw err;
|
|
@@ -253,6 +264,7 @@ class ArgParser {
|
|
|
253
264
|
pioInstall: parsed["pio-install"],
|
|
254
265
|
pioUninstall: parsed["pio-uninstall"],
|
|
255
266
|
debugMode: parsed.debug,
|
|
267
|
+
serveMode: parsed.serve,
|
|
256
268
|
};
|
|
257
269
|
}
|
|
258
270
|
}
|
package/src/cli/CleanCommand.ts
CHANGED
|
@@ -12,6 +12,45 @@ import PathResolver from "../transpiler/data/PathResolver";
|
|
|
12
12
|
* Command to clean generated output files
|
|
13
13
|
*/
|
|
14
14
|
class CleanCommand {
|
|
15
|
+
/**
|
|
16
|
+
* Discover CNX files from inputs.
|
|
17
|
+
* Returns null if none found or on error.
|
|
18
|
+
*/
|
|
19
|
+
private static discoverCnxFiles(inputs: string[]): string[] | null {
|
|
20
|
+
try {
|
|
21
|
+
const cnxFiles = InputExpansion.expandInputs(inputs);
|
|
22
|
+
if (cnxFiles.length === 0) {
|
|
23
|
+
console.log("No .cnx files found. Nothing to clean.");
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return cnxFiles;
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error(`Error: ${error}`);
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Delete generated files for a given set of extensions.
|
|
35
|
+
*/
|
|
36
|
+
private static deleteGeneratedFiles(
|
|
37
|
+
baseName: string,
|
|
38
|
+
relativePath: string | null,
|
|
39
|
+
targetDir: string,
|
|
40
|
+
extensions: string[],
|
|
41
|
+
): number {
|
|
42
|
+
let count = 0;
|
|
43
|
+
for (const ext of extensions) {
|
|
44
|
+
const outputPath = relativePath
|
|
45
|
+
? join(targetDir, relativePath.replace(/\.cnx$|\.cnext$/, ext))
|
|
46
|
+
: join(targetDir, baseName + ext);
|
|
47
|
+
if (this.deleteIfExists(outputPath)) {
|
|
48
|
+
count++;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return count;
|
|
52
|
+
}
|
|
53
|
+
|
|
15
54
|
/**
|
|
16
55
|
* Execute the clean command
|
|
17
56
|
*
|
|
@@ -24,32 +63,19 @@ class CleanCommand {
|
|
|
24
63
|
outDir: string,
|
|
25
64
|
headerOutDir?: string,
|
|
26
65
|
): void {
|
|
27
|
-
// If no outDir specified, we can't determine where to clean
|
|
28
66
|
if (!outDir) {
|
|
29
67
|
console.log("No output directory specified. Nothing to clean.");
|
|
30
68
|
return;
|
|
31
69
|
}
|
|
32
70
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
cnxFiles = InputExpansion.expandInputs(inputs);
|
|
37
|
-
} catch (error) {
|
|
38
|
-
console.error(`Error: ${error}`);
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (cnxFiles.length === 0) {
|
|
43
|
-
console.log("No .cnx files found. Nothing to clean.");
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
71
|
+
const cnxFiles = this.discoverCnxFiles(inputs);
|
|
72
|
+
if (!cnxFiles) return;
|
|
46
73
|
|
|
47
74
|
const resolvedOutDir = resolve(outDir);
|
|
48
75
|
const resolvedHeaderDir = headerOutDir
|
|
49
76
|
? resolve(headerOutDir)
|
|
50
77
|
: resolvedOutDir;
|
|
51
78
|
|
|
52
|
-
// Issue #586: Use PathResolver for consistent path calculation
|
|
53
79
|
const pathResolver = new PathResolver({
|
|
54
80
|
inputs,
|
|
55
81
|
outDir,
|
|
@@ -58,39 +84,25 @@ class CleanCommand {
|
|
|
58
84
|
|
|
59
85
|
let deletedCount = 0;
|
|
60
86
|
|
|
61
|
-
// For each .cnx file, calculate and delete generated files
|
|
62
87
|
for (const cnxFile of cnxFiles) {
|
|
63
88
|
const baseName = basename(cnxFile).replace(/\.cnx$|\.cnext$/, "");
|
|
64
|
-
|
|
65
|
-
// Calculate relative path from input directories
|
|
66
89
|
const relativePath = pathResolver.getRelativePathFromInputs(cnxFile);
|
|
67
90
|
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const headerPath = relativePath
|
|
84
|
-
? join(
|
|
85
|
-
resolvedHeaderDir,
|
|
86
|
-
relativePath.replace(/\.cnx$|\.cnext$/, ext),
|
|
87
|
-
)
|
|
88
|
-
: join(resolvedHeaderDir, baseName + ext);
|
|
89
|
-
|
|
90
|
-
if (this.deleteIfExists(headerPath)) {
|
|
91
|
-
deletedCount++;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
91
|
+
// Delete code files (.c and .cpp)
|
|
92
|
+
deletedCount += this.deleteGeneratedFiles(
|
|
93
|
+
baseName,
|
|
94
|
+
relativePath,
|
|
95
|
+
resolvedOutDir,
|
|
96
|
+
[".c", ".cpp"],
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// Delete header files (.h and .hpp)
|
|
100
|
+
deletedCount += this.deleteGeneratedFiles(
|
|
101
|
+
baseName,
|
|
102
|
+
relativePath,
|
|
103
|
+
resolvedHeaderDir,
|
|
104
|
+
[".h", ".hpp"],
|
|
105
|
+
);
|
|
94
106
|
}
|
|
95
107
|
|
|
96
108
|
if (deletedCount === 0) {
|
package/src/cli/Cli.ts
CHANGED
|
@@ -38,6 +38,16 @@ class Cli {
|
|
|
38
38
|
return { shouldRun: false, exitCode: 0 };
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
// Early exit for serve mode (JSON-RPC server)
|
|
42
|
+
if (args.serveMode) {
|
|
43
|
+
return {
|
|
44
|
+
shouldRun: false,
|
|
45
|
+
exitCode: 0,
|
|
46
|
+
serveMode: true,
|
|
47
|
+
serveDebug: args.verbose,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
41
51
|
// Load config file (searches up from input file directory)
|
|
42
52
|
const configDir =
|
|
43
53
|
args.inputFiles.length > 0
|
|
@@ -6,6 +6,31 @@
|
|
|
6
6
|
import { resolve } from "node:path";
|
|
7
7
|
import { existsSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Resolved paths for PlatformIO project.
|
|
11
|
+
*/
|
|
12
|
+
interface IPioProjectPaths {
|
|
13
|
+
pioIniPath: string;
|
|
14
|
+
scriptPath: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get PlatformIO project paths and validate that platformio.ini exists.
|
|
19
|
+
* Exits with error if not in a PlatformIO project directory.
|
|
20
|
+
*/
|
|
21
|
+
function getPioProjectPaths(): IPioProjectPaths {
|
|
22
|
+
const pioIniPath = resolve(process.cwd(), "platformio.ini");
|
|
23
|
+
const scriptPath = resolve(process.cwd(), "cnext_build.py");
|
|
24
|
+
|
|
25
|
+
if (!existsSync(pioIniPath)) {
|
|
26
|
+
console.error("Error: platformio.ini not found in current directory");
|
|
27
|
+
console.error("Run this command from your PlatformIO project root");
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return { pioIniPath, scriptPath };
|
|
32
|
+
}
|
|
33
|
+
|
|
9
34
|
/**
|
|
10
35
|
* PlatformIO integration commands
|
|
11
36
|
*/
|
|
@@ -15,15 +40,7 @@ class PlatformIOCommand {
|
|
|
15
40
|
* Creates cnext_build.py and modifies platformio.ini
|
|
16
41
|
*/
|
|
17
42
|
static install(): void {
|
|
18
|
-
const pioIniPath =
|
|
19
|
-
const scriptPath = resolve(process.cwd(), "cnext_build.py");
|
|
20
|
-
|
|
21
|
-
// Check if platformio.ini exists
|
|
22
|
-
if (!existsSync(pioIniPath)) {
|
|
23
|
-
console.error("Error: platformio.ini not found in current directory");
|
|
24
|
-
console.error("Run this command from your PlatformIO project root");
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
43
|
+
const { pioIniPath, scriptPath } = getPioProjectPaths();
|
|
27
44
|
|
|
28
45
|
// Create cnext_build.py script
|
|
29
46
|
const buildScript = `Import("env")
|
|
@@ -110,15 +127,7 @@ env.AddPreAction("buildprog", transpile_cnext)
|
|
|
110
127
|
* Deletes cnext_build.py and removes extra_scripts from platformio.ini
|
|
111
128
|
*/
|
|
112
129
|
static uninstall(): void {
|
|
113
|
-
const pioIniPath =
|
|
114
|
-
const scriptPath = resolve(process.cwd(), "cnext_build.py");
|
|
115
|
-
|
|
116
|
-
// Check if platformio.ini exists
|
|
117
|
-
if (!existsSync(pioIniPath)) {
|
|
118
|
-
console.error("Error: platformio.ini not found in current directory");
|
|
119
|
-
console.error("Run this command from your PlatformIO project root");
|
|
120
|
-
process.exit(1);
|
|
121
|
-
}
|
|
130
|
+
const { pioIniPath, scriptPath } = getPioProjectPaths();
|
|
122
131
|
|
|
123
132
|
let hasChanges = false;
|
|
124
133
|
|
package/src/cli/Runner.ts
CHANGED
|
@@ -9,6 +9,19 @@ import InputExpansion from "../transpiler/data/InputExpansion";
|
|
|
9
9
|
import Transpiler from "../transpiler/Transpiler";
|
|
10
10
|
import ICliConfig from "./types/ICliConfig";
|
|
11
11
|
import ResultPrinter from "./ResultPrinter";
|
|
12
|
+
import ITranspilerResult from "../transpiler/types/ITranspilerResult";
|
|
13
|
+
|
|
14
|
+
/** Result of categorizing inputs into directories and files */
|
|
15
|
+
interface ICategorizedInputs {
|
|
16
|
+
srcDirs: string[];
|
|
17
|
+
explicitFiles: string[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Result of determining output path */
|
|
21
|
+
interface IOutputPathResult {
|
|
22
|
+
outDir: string;
|
|
23
|
+
explicitOutputFile: string | null;
|
|
24
|
+
}
|
|
12
25
|
|
|
13
26
|
/**
|
|
14
27
|
* Execute the transpiler
|
|
@@ -19,24 +32,63 @@ class Runner {
|
|
|
19
32
|
* @param config - CLI configuration
|
|
20
33
|
*/
|
|
21
34
|
static async execute(config: ICliConfig): Promise<void> {
|
|
22
|
-
|
|
23
|
-
|
|
35
|
+
const { srcDirs, explicitFiles } = this._categorizeInputs(config.inputs);
|
|
36
|
+
const files = this._expandInputFiles(config.inputs);
|
|
37
|
+
const { outDir, explicitOutputFile } = this._determineOutputPath(
|
|
38
|
+
config,
|
|
39
|
+
files,
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const pipeline = new Transpiler({
|
|
43
|
+
inputs: [...srcDirs, ...explicitFiles],
|
|
44
|
+
includeDirs: config.includeDirs,
|
|
45
|
+
outDir,
|
|
46
|
+
headerOutDir: config.headerOutDir,
|
|
47
|
+
basePath: config.basePath,
|
|
48
|
+
preprocess: config.preprocess,
|
|
49
|
+
defines: config.defines,
|
|
50
|
+
cppRequired: config.cppRequired,
|
|
51
|
+
noCache: config.noCache,
|
|
52
|
+
parseOnly: config.parseOnly,
|
|
53
|
+
target: config.target,
|
|
54
|
+
debugMode: config.debugMode,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const result = await pipeline.run();
|
|
58
|
+
this._renameOutputIfNeeded(result, explicitOutputFile);
|
|
59
|
+
|
|
60
|
+
ResultPrinter.print(result);
|
|
61
|
+
process.exit(result.success ? 0 : 1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Categorize inputs into directories and explicit files.
|
|
66
|
+
*/
|
|
67
|
+
private static _categorizeInputs(inputs: string[]): ICategorizedInputs {
|
|
24
68
|
const srcDirs: string[] = [];
|
|
25
69
|
const explicitFiles: string[] = [];
|
|
26
70
|
|
|
27
|
-
for (const input of
|
|
71
|
+
for (const input of inputs) {
|
|
28
72
|
const resolvedPath = resolve(input);
|
|
29
|
-
|
|
73
|
+
const isDir =
|
|
74
|
+
existsSync(resolvedPath) && statSync(resolvedPath).isDirectory();
|
|
75
|
+
if (isDir) {
|
|
30
76
|
srcDirs.push(resolvedPath);
|
|
31
77
|
} else {
|
|
32
78
|
explicitFiles.push(resolvedPath);
|
|
33
79
|
}
|
|
34
80
|
}
|
|
35
81
|
|
|
36
|
-
|
|
82
|
+
return { srcDirs, explicitFiles };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Expand input paths to .cnx files.
|
|
87
|
+
*/
|
|
88
|
+
private static _expandInputFiles(inputs: string[]): string[] {
|
|
37
89
|
let files: string[];
|
|
38
90
|
try {
|
|
39
|
-
files = InputExpansion.expandInputs(
|
|
91
|
+
files = InputExpansion.expandInputs(inputs);
|
|
40
92
|
} catch (error) {
|
|
41
93
|
console.error(`Error: ${error}`);
|
|
42
94
|
process.exit(1);
|
|
@@ -47,77 +99,71 @@ class Runner {
|
|
|
47
99
|
process.exit(1);
|
|
48
100
|
}
|
|
49
101
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
let outDir: string;
|
|
53
|
-
let explicitOutputFile: string | null = null;
|
|
102
|
+
return files;
|
|
103
|
+
}
|
|
54
104
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
? statSync(config.outputPath)
|
|
65
|
-
: null;
|
|
66
|
-
if (stats?.isDirectory() || config.outputPath.endsWith("/")) {
|
|
67
|
-
outDir = config.outputPath;
|
|
68
|
-
} else if (isExplicitFile) {
|
|
69
|
-
// Explicit output file path
|
|
70
|
-
if (files.length > 1) {
|
|
71
|
-
console.error(
|
|
72
|
-
"Error: Cannot use explicit output filename with multiple input files",
|
|
73
|
-
);
|
|
74
|
-
console.error("Use a directory path instead: -o <directory>/");
|
|
75
|
-
process.exit(1);
|
|
76
|
-
}
|
|
77
|
-
outDir = dirname(config.outputPath);
|
|
78
|
-
explicitOutputFile = resolve(config.outputPath);
|
|
79
|
-
} else {
|
|
80
|
-
outDir = config.outputPath;
|
|
81
|
-
}
|
|
82
|
-
} else {
|
|
83
|
-
// No -o flag: use same directory as first input file
|
|
84
|
-
outDir = dirname(files[0]);
|
|
105
|
+
/**
|
|
106
|
+
* Determine output directory and explicit filename from config.
|
|
107
|
+
*/
|
|
108
|
+
private static _determineOutputPath(
|
|
109
|
+
config: ICliConfig,
|
|
110
|
+
files: string[],
|
|
111
|
+
): IOutputPathResult {
|
|
112
|
+
if (!config.outputPath) {
|
|
113
|
+
return { outDir: dirname(files[0]), explicitOutputFile: null };
|
|
85
114
|
}
|
|
86
115
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const pipelineInputs = [...srcDirs, ...explicitFiles];
|
|
90
|
-
const pipeline = new Transpiler({
|
|
91
|
-
inputs: pipelineInputs,
|
|
92
|
-
includeDirs: config.includeDirs,
|
|
93
|
-
outDir,
|
|
94
|
-
headerOutDir: config.headerOutDir,
|
|
95
|
-
basePath: config.basePath,
|
|
96
|
-
preprocess: config.preprocess,
|
|
97
|
-
defines: config.defines,
|
|
98
|
-
cppRequired: config.cppRequired,
|
|
99
|
-
noCache: config.noCache,
|
|
100
|
-
parseOnly: config.parseOnly,
|
|
101
|
-
target: config.target,
|
|
102
|
-
debugMode: config.debugMode,
|
|
103
|
-
});
|
|
116
|
+
const isExplicitFile =
|
|
117
|
+
/\.(c|cpp)$/.test(config.outputPath) && !config.outputPath.endsWith("/");
|
|
104
118
|
|
|
105
|
-
|
|
106
|
-
|
|
119
|
+
const stats = existsSync(config.outputPath)
|
|
120
|
+
? statSync(config.outputPath)
|
|
121
|
+
: null;
|
|
107
122
|
|
|
108
|
-
//
|
|
109
|
-
if (
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
123
|
+
// Directory path
|
|
124
|
+
if (stats?.isDirectory() || config.outputPath.endsWith("/")) {
|
|
125
|
+
return { outDir: config.outputPath, explicitOutputFile: null };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Explicit output file
|
|
129
|
+
if (isExplicitFile) {
|
|
130
|
+
if (files.length > 1) {
|
|
131
|
+
console.error(
|
|
132
|
+
"Error: Cannot use explicit output filename with multiple input files",
|
|
133
|
+
);
|
|
134
|
+
console.error("Use a directory path instead: -o <directory>/");
|
|
135
|
+
process.exit(1);
|
|
116
136
|
}
|
|
137
|
+
return {
|
|
138
|
+
outDir: dirname(config.outputPath),
|
|
139
|
+
explicitOutputFile: resolve(config.outputPath),
|
|
140
|
+
};
|
|
117
141
|
}
|
|
118
142
|
|
|
119
|
-
|
|
120
|
-
|
|
143
|
+
// Default: treat as directory
|
|
144
|
+
return { outDir: config.outputPath, explicitOutputFile: null };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Rename output file if explicit filename was specified.
|
|
149
|
+
*/
|
|
150
|
+
private static _renameOutputIfNeeded(
|
|
151
|
+
result: ITranspilerResult,
|
|
152
|
+
explicitOutputFile: string | null,
|
|
153
|
+
): void {
|
|
154
|
+
if (
|
|
155
|
+
!explicitOutputFile ||
|
|
156
|
+
!result.success ||
|
|
157
|
+
result.outputFiles.length === 0
|
|
158
|
+
) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const generatedFile = result.outputFiles[0];
|
|
163
|
+
if (generatedFile !== explicitOutputFile) {
|
|
164
|
+
renameSync(generatedFile, explicitOutputFile);
|
|
165
|
+
result.outputFiles[0] = explicitOutputFile;
|
|
166
|
+
}
|
|
121
167
|
}
|
|
122
168
|
}
|
|
123
169
|
|
|
@@ -281,20 +281,24 @@ describe("ArgParser", () => {
|
|
|
281
281
|
expect(consoleLogSpy).toHaveBeenCalled();
|
|
282
282
|
});
|
|
283
283
|
|
|
284
|
-
it("
|
|
285
|
-
// yargs
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
expect(
|
|
290
|
-
|
|
291
|
-
|
|
284
|
+
it("shows helpful message for -I flag (GCC-style)", () => {
|
|
285
|
+
// Use "-I path" with a space (yargs parses "-I<dir>" as multiple short flags)
|
|
286
|
+
expect(() => ArgParser.parse(argv("input.cnx", "-I", "path"))).toThrow(
|
|
287
|
+
"process.exit(1)",
|
|
288
|
+
);
|
|
289
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
290
|
+
"Error: Unknown flag '-I...'",
|
|
291
|
+
);
|
|
292
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
293
|
+
" Did you mean: --include <dir>",
|
|
294
|
+
);
|
|
292
295
|
});
|
|
293
296
|
|
|
294
|
-
it("
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
297
|
+
it("rejects unknown flags with error", () => {
|
|
298
|
+
expect(() =>
|
|
299
|
+
ArgParser.parse(argv("input.cnx", "--unknown-flag")),
|
|
300
|
+
).toThrow("process.exit(1)");
|
|
301
|
+
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
298
302
|
});
|
|
299
303
|
});
|
|
300
304
|
});
|
|
@@ -50,6 +50,7 @@ describe("Cli", () => {
|
|
|
50
50
|
pioInstall: false,
|
|
51
51
|
pioUninstall: false,
|
|
52
52
|
debugMode: false,
|
|
53
|
+
serveMode: false,
|
|
53
54
|
};
|
|
54
55
|
|
|
55
56
|
// Default mocks
|
|
@@ -95,6 +96,17 @@ describe("Cli", () => {
|
|
|
95
96
|
expect(result.exitCode).toBe(0);
|
|
96
97
|
});
|
|
97
98
|
|
|
99
|
+
it("handles --serve flag", () => {
|
|
100
|
+
mockParsedArgs.serveMode = true;
|
|
101
|
+
vi.mocked(ArgParser.parse).mockReturnValue(mockParsedArgs);
|
|
102
|
+
|
|
103
|
+
const result = Cli.run();
|
|
104
|
+
|
|
105
|
+
expect(result.shouldRun).toBe(false);
|
|
106
|
+
expect(result.exitCode).toBe(0);
|
|
107
|
+
expect(result.serveMode).toBe(true);
|
|
108
|
+
});
|
|
109
|
+
|
|
98
110
|
it("handles --config flag", () => {
|
|
99
111
|
mockParsedArgs.showConfig = true;
|
|
100
112
|
vi.mocked(ArgParser.parse).mockReturnValue(mockParsedArgs);
|
|
@@ -294,6 +306,7 @@ describe("Cli", () => {
|
|
|
294
306
|
showConfig: false,
|
|
295
307
|
pioInstall: false,
|
|
296
308
|
pioUninstall: false,
|
|
309
|
+
serveMode: false,
|
|
297
310
|
};
|
|
298
311
|
vi.mocked(ArgParser.parse).mockReturnValue(mockParsedArgs);
|
|
299
312
|
|