c-next 0.2.13 → 0.2.15
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/dist/index.js +106 -50
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
- package/src/cli/ArgParser.ts +2 -6
- package/src/cli/PlatformIOCommand.ts +27 -29
- package/src/cli/__tests__/PlatformIOCommand.test.ts +23 -0
- package/src/transpiler/Transpiler.ts +10 -2
- package/src/transpiler/data/IncludeResolver.ts +11 -1
- package/src/transpiler/data/__tests__/IncludeResolver.test.ts +50 -0
- package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +28 -0
- package/src/transpiler/logic/symbols/c/collectors/VariableCollector.ts +12 -1
- package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +20 -13
- package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +20 -6
- package/src/transpiler/state/CodeGenState.ts +45 -3
- package/src/transpiler/state/__tests__/CodeGenState.test.ts +145 -3
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import { hideBin } from "yargs/helpers";
|
|
|
11
11
|
// package.json
|
|
12
12
|
var package_default = {
|
|
13
13
|
name: "c-next",
|
|
14
|
-
version: "0.2.
|
|
14
|
+
version: "0.2.15",
|
|
15
15
|
description: "A safer C for embedded systems development. Transpiles to clean, readable C.",
|
|
16
16
|
packageManager: "npm@11.9.0",
|
|
17
17
|
type: "module",
|
|
@@ -195,10 +195,8 @@ var ConfigPrinter_default = ConfigPrinter;
|
|
|
195
195
|
function configureYargs(args, argv) {
|
|
196
196
|
return yargs(args).scriptName("cnext").usage(
|
|
197
197
|
`Usage:
|
|
198
|
-
cnext <file.cnx>
|
|
198
|
+
cnext <file.cnx> Entry point file (follows includes)
|
|
199
199
|
cnext <file.cnx> -o <output.c> Single file with explicit output
|
|
200
|
-
cnext <files...> -o <dir> Multi-file mode
|
|
201
|
-
cnext <dir> Directory mode (recursive)
|
|
202
200
|
|
|
203
201
|
A safer C for embedded systems development.`
|
|
204
202
|
).option("o", {
|
|
@@ -275,10 +273,8 @@ A safer C for embedded systems development.`
|
|
|
275
273
|
default: false
|
|
276
274
|
}).epilogue(
|
|
277
275
|
`Examples:
|
|
278
|
-
cnext main.cnx
|
|
276
|
+
cnext src/main.cnx # Entry point (follows includes)
|
|
279
277
|
cnext main.cnx -o build/main.c # Explicit output path
|
|
280
|
-
cnext src/*.cnx -o build/ # Multiple files to directory
|
|
281
|
-
cnext src/ # Compile all .cnx files in src/ (recursive)
|
|
282
278
|
|
|
283
279
|
Target platforms: teensy41, cortex-m7, cortex-m4, cortex-m3, cortex-m0+, cortex-m0, avr
|
|
284
280
|
|
|
@@ -415,37 +411,36 @@ var PlatformIOCommand = class {
|
|
|
415
411
|
*/
|
|
416
412
|
static install() {
|
|
417
413
|
const { pioIniPath, scriptPath } = getPioProjectPaths();
|
|
418
|
-
const buildScript = `Import("env")
|
|
414
|
+
const buildScript = String.raw`Import("env")
|
|
419
415
|
import subprocess
|
|
420
416
|
import sys
|
|
421
417
|
from pathlib import Path
|
|
422
418
|
|
|
423
419
|
def transpile_cnext():
|
|
424
|
-
"""Transpile
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
if not src_dir.exists():
|
|
420
|
+
"""Transpile from main.cnx entry point — cnext follows includes"""
|
|
421
|
+
entry = Path("src/main.cnx")
|
|
422
|
+
if not entry.exists():
|
|
428
423
|
return
|
|
429
424
|
|
|
430
|
-
|
|
431
|
-
if not cnx_files:
|
|
432
|
-
return
|
|
433
|
-
|
|
434
|
-
print(f"Transpiling {len(cnx_files)} c-next files...")
|
|
425
|
+
print("Transpiling from main.cnx...")
|
|
435
426
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
427
|
+
try:
|
|
428
|
+
result = subprocess.run(
|
|
429
|
+
["cnext", str(entry)],
|
|
430
|
+
check=True,
|
|
431
|
+
capture_output=True,
|
|
432
|
+
text=True
|
|
433
|
+
)
|
|
434
|
+
if result.stdout:
|
|
435
|
+
lines = result.stdout.strip().split("\n")
|
|
436
|
+
for line in lines:
|
|
437
|
+
if line.startswith(("Compiled", "Collected", "Generated")):
|
|
438
|
+
print(f" {line}")
|
|
439
|
+
print(" ✓ Transpilation complete")
|
|
440
|
+
except subprocess.CalledProcessError as e:
|
|
441
|
+
print(f" ✗ Transpilation failed")
|
|
442
|
+
print(e.stderr)
|
|
443
|
+
sys.exit(1)
|
|
449
444
|
|
|
450
445
|
# Run transpilation at import time (before compilation starts)
|
|
451
446
|
transpile_cnext()
|
|
@@ -475,13 +470,12 @@ transpile_cnext()
|
|
|
475
470
|
console.log("\u2713 PlatformIO integration configured!");
|
|
476
471
|
console.log("");
|
|
477
472
|
console.log("Next steps:");
|
|
478
|
-
console.log(
|
|
479
|
-
|
|
480
|
-
);
|
|
481
|
-
console.log(" 2. Run: pio run");
|
|
473
|
+
console.log(" 1. Create src/main.cnx as your entry point");
|
|
474
|
+
console.log(" 2. Use #include to pull in other .cnx files");
|
|
475
|
+
console.log(" 3. Run: pio run");
|
|
482
476
|
console.log("");
|
|
483
477
|
console.log(
|
|
484
|
-
"The transpiler will
|
|
478
|
+
"The transpiler will follow includes from main.cnx automatically."
|
|
485
479
|
);
|
|
486
480
|
console.log("Commit both .cnx and generated .c files to version control.");
|
|
487
481
|
}
|
|
@@ -112682,7 +112676,7 @@ var DEFAULT_TARGET = {
|
|
|
112682
112676
|
hasLdrexStrex: false,
|
|
112683
112677
|
hasBasepri: false
|
|
112684
112678
|
};
|
|
112685
|
-
var CodeGenState = class {
|
|
112679
|
+
var CodeGenState = class _CodeGenState {
|
|
112686
112680
|
// ===========================================================================
|
|
112687
112681
|
// GENERATOR REFERENCE (for handler access)
|
|
112688
112682
|
// ===========================================================================
|
|
@@ -113049,6 +113043,19 @@ var CodeGenState = class {
|
|
|
113049
113043
|
if (symbol?.kind === "variable" && symbol.type) {
|
|
113050
113044
|
return this.convertTSymbolToTypeInfo(symbol);
|
|
113051
113045
|
}
|
|
113046
|
+
const cSymbol = this.symbolTable.getCSymbol(name);
|
|
113047
|
+
if (cSymbol?.kind === "variable" && cSymbol.type) {
|
|
113048
|
+
const baseType = _CodeGenState.stripTrailingPointers(cSymbol.type);
|
|
113049
|
+
if (this.symbolTable.isTypedefStructType(baseType) || this.symbolTable.getStructFields(baseType)) {
|
|
113050
|
+
return {
|
|
113051
|
+
baseType,
|
|
113052
|
+
bitWidth: 0,
|
|
113053
|
+
isArray: cSymbol.isArray || false,
|
|
113054
|
+
isConst: cSymbol.isConst || false,
|
|
113055
|
+
isPointer: cSymbol.type.endsWith("*")
|
|
113056
|
+
};
|
|
113057
|
+
}
|
|
113058
|
+
}
|
|
113052
113059
|
return void 0;
|
|
113053
113060
|
}
|
|
113054
113061
|
/**
|
|
@@ -113067,7 +113074,15 @@ var CodeGenState = class {
|
|
|
113067
113074
|
return true;
|
|
113068
113075
|
}
|
|
113069
113076
|
const symbol = this.symbolTable.getTSymbol(name);
|
|
113070
|
-
|
|
113077
|
+
if (symbol?.kind === "variable" && symbol.type !== void 0) {
|
|
113078
|
+
return true;
|
|
113079
|
+
}
|
|
113080
|
+
const cSymbol = this.symbolTable.getCSymbol(name);
|
|
113081
|
+
if (cSymbol?.kind === "variable" && cSymbol.type) {
|
|
113082
|
+
const baseType = _CodeGenState.stripTrailingPointers(cSymbol.type);
|
|
113083
|
+
return this.symbolTable.isTypedefStructType(baseType) || !!this.symbolTable.getStructFields(baseType);
|
|
113084
|
+
}
|
|
113085
|
+
return false;
|
|
113071
113086
|
}
|
|
113072
113087
|
/**
|
|
113073
113088
|
* Set variable type info in the local registry.
|
|
@@ -113093,6 +113108,17 @@ var CodeGenState = class {
|
|
|
113093
113108
|
* Convert a TSymbol IVariableSymbol to TTypeInfo for unified type lookups.
|
|
113094
113109
|
* ADR-055 Phase 7: Works with typed TSymbol instead of ISymbol.
|
|
113095
113110
|
*/
|
|
113111
|
+
/**
|
|
113112
|
+
* Strip trailing pointer stars from a C type string (e.g., "font_t*" → "font_t").
|
|
113113
|
+
* Uses string operations instead of regex to avoid SonarCloud ReDoS flag (S5852).
|
|
113114
|
+
*/
|
|
113115
|
+
static stripTrailingPointers(type) {
|
|
113116
|
+
let end = type.length;
|
|
113117
|
+
while (end > 0 && type[end - 1] === "*") {
|
|
113118
|
+
end--;
|
|
113119
|
+
}
|
|
113120
|
+
return type.slice(0, end).trim();
|
|
113121
|
+
}
|
|
113096
113122
|
static convertTSymbolToTypeInfo(symbol) {
|
|
113097
113123
|
const typeName = TypeResolver_default.getTypeName(symbol.type);
|
|
113098
113124
|
const stringPattern = /^string<(\d+)>$/;
|
|
@@ -133386,16 +133412,23 @@ var HeaderGeneratorUtils = class _HeaderGeneratorUtils {
|
|
|
133386
133412
|
}
|
|
133387
133413
|
}
|
|
133388
133414
|
const userIncludeSet = new Set(options.userIncludes ?? []);
|
|
133389
|
-
|
|
133390
|
-
|
|
133391
|
-
|
|
133392
|
-
|
|
133393
|
-
|
|
133394
|
-
|
|
133415
|
+
const extractStem = (inc) => {
|
|
133416
|
+
const match = /["<]([^">]+)[">]/.exec(inc);
|
|
133417
|
+
if (!match) return inc;
|
|
133418
|
+
return match[1].replace(/^.*\//, "").replace(/\.(?:h|hpp)$/, "");
|
|
133419
|
+
};
|
|
133420
|
+
const userIncludeStems = new Set(
|
|
133421
|
+
(options.userIncludes ?? []).map(extractStem)
|
|
133422
|
+
);
|
|
133395
133423
|
for (const directive of headersToInclude) {
|
|
133396
|
-
if (
|
|
133397
|
-
|
|
133424
|
+
if (userIncludeSet.has(directive)) {
|
|
133425
|
+
continue;
|
|
133426
|
+
}
|
|
133427
|
+
const stem = extractStem(directive);
|
|
133428
|
+
if (stem && userIncludeStems.has(stem)) {
|
|
133429
|
+
continue;
|
|
133398
133430
|
}
|
|
133431
|
+
lines.push(directive);
|
|
133399
133432
|
}
|
|
133400
133433
|
const hasIncludes = options.includeSystemHeaders !== false || options.userIncludes && options.userIncludes.length > 0 || headersToInclude.size > 0;
|
|
133401
133434
|
if (hasIncludes) {
|
|
@@ -135948,6 +135981,8 @@ var VariableCollector2 = class {
|
|
|
135948
135981
|
*/
|
|
135949
135982
|
static collect(name, baseType, declarator, sourceFile, line, isExtern) {
|
|
135950
135983
|
const arrayDimensions = declarator ? DeclaratorUtils_default.extractArrayDimensions(declarator) : [];
|
|
135984
|
+
const hasPointer = declarator?.pointer?.() !== null && declarator?.pointer?.() !== void 0;
|
|
135985
|
+
const resolvedType = hasPointer ? `${baseType}*` : baseType;
|
|
135951
135986
|
return {
|
|
135952
135987
|
kind: "variable",
|
|
135953
135988
|
name,
|
|
@@ -135955,7 +135990,7 @@ var VariableCollector2 = class {
|
|
|
135955
135990
|
sourceLine: line,
|
|
135956
135991
|
sourceLanguage: ESourceLanguage_default.C,
|
|
135957
135992
|
isExported: !isExtern,
|
|
135958
|
-
type:
|
|
135993
|
+
type: resolvedType,
|
|
135959
135994
|
isArray: arrayDimensions.length > 0,
|
|
135960
135995
|
arrayDimensions: arrayDimensions.length > 0 ? arrayDimensions : void 0,
|
|
135961
135996
|
isExtern
|
|
@@ -135965,6 +136000,10 @@ var VariableCollector2 = class {
|
|
|
135965
136000
|
* Collect a variable from declaration specifiers (when identifier appears as typedefName).
|
|
135966
136001
|
* This handles the C grammar ambiguity where variable names can be parsed as typedef names.
|
|
135967
136002
|
*
|
|
136003
|
+
* Note: No pointer detection here — this path handles declarations without an
|
|
136004
|
+
* initDeclaratorList. Pointer declarations (e.g., `font_t *ptr`) always produce
|
|
136005
|
+
* an initDeclaratorList (the `*` creates a declarator), so they go through collect().
|
|
136006
|
+
*
|
|
135968
136007
|
* @param name Variable name
|
|
135969
136008
|
* @param baseType Variable type
|
|
135970
136009
|
* @param sourceFile Source file path
|
|
@@ -137760,9 +137799,16 @@ var DependencyGraph_default = DependencyGraph;
|
|
|
137760
137799
|
// src/transpiler/data/IncludeResolver.ts
|
|
137761
137800
|
var defaultFs5 = NodeFileSystem_default.instance;
|
|
137762
137801
|
var IncludeResolver = class _IncludeResolver {
|
|
137763
|
-
|
|
137802
|
+
/**
|
|
137803
|
+
* @param cppMode Controls .h vs .hpp extension for .cnx include directives.
|
|
137804
|
+
* Note: In the Transpiler, cppDetected may change after IncludeResolver runs
|
|
137805
|
+
* (e.g., when a .hpp header is discovered during Stage 2). HeaderGeneratorUtils
|
|
137806
|
+
* uses stem-based dedup to handle any resulting .h/.hpp mismatch.
|
|
137807
|
+
*/
|
|
137808
|
+
constructor(searchPaths, fs = defaultFs5, cppMode = false) {
|
|
137764
137809
|
this.searchPaths = searchPaths;
|
|
137765
137810
|
this.fs = fs;
|
|
137811
|
+
this.cppMode = cppMode;
|
|
137766
137812
|
}
|
|
137767
137813
|
/**
|
|
137768
137814
|
* Type helper for accessing IResolvedIncludes externally.
|
|
@@ -137771,6 +137817,7 @@ var IncludeResolver = class _IncludeResolver {
|
|
|
137771
137817
|
static _resolvedIncludesType = void 0;
|
|
137772
137818
|
resolvedPaths = /* @__PURE__ */ new Set();
|
|
137773
137819
|
fs;
|
|
137820
|
+
cppMode;
|
|
137774
137821
|
/**
|
|
137775
137822
|
* Extract includes from source content and resolve them to files
|
|
137776
137823
|
*
|
|
@@ -137835,7 +137882,8 @@ var IncludeResolver = class _IncludeResolver {
|
|
|
137835
137882
|
}
|
|
137836
137883
|
if (file.type === EFileType_default.CNext) {
|
|
137837
137884
|
result.cnextIncludes.push(file);
|
|
137838
|
-
const
|
|
137885
|
+
const ext = this.cppMode ? ".hpp" : ".h";
|
|
137886
|
+
const headerPath = includeInfo.path.replace(/\.cnx$|\.cnext$/, ext);
|
|
137839
137887
|
const directive = includeInfo.isLocal ? `#include "${headerPath}"` : `#include <${headerPath}>`;
|
|
137840
137888
|
result.headerIncludeDirectives.set(absolutePath, directive);
|
|
137841
137889
|
}
|
|
@@ -141708,7 +141756,11 @@ var Transpiler = class {
|
|
|
141708
141756
|
void 0,
|
|
141709
141757
|
this.fs
|
|
141710
141758
|
);
|
|
141711
|
-
const resolver = new IncludeResolver_default(
|
|
141759
|
+
const resolver = new IncludeResolver_default(
|
|
141760
|
+
searchPaths,
|
|
141761
|
+
this.fs,
|
|
141762
|
+
this.cppDetected
|
|
141763
|
+
);
|
|
141712
141764
|
const resolved = resolver.resolve(source, sourcePath);
|
|
141713
141765
|
this.warnings.push(...resolved.warnings);
|
|
141714
141766
|
const { headers: allHeaders, warnings: headerWarnings } = IncludeResolver_default.resolveHeadersTransitively(
|
|
@@ -141980,7 +142032,11 @@ var Transpiler = class {
|
|
|
141980
142032
|
void 0,
|
|
141981
142033
|
this.fs
|
|
141982
142034
|
);
|
|
141983
|
-
const resolver = new IncludeResolver_default(
|
|
142035
|
+
const resolver = new IncludeResolver_default(
|
|
142036
|
+
searchPaths,
|
|
142037
|
+
this.fs,
|
|
142038
|
+
this.cppDetected
|
|
142039
|
+
);
|
|
141984
142040
|
const resolved = resolver.resolve(content, cnxFile.path);
|
|
141985
142041
|
this._collectHeaders(resolved, cnextBaseNames, headerSet);
|
|
141986
142042
|
this._processCnextIncludes(
|