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 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.13",
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> Single file (outputs file.c)
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 # Outputs main.c (same dir)
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 all .cnx files before build"""
425
- # Find all .cnx files in src directory
426
- src_dir = Path("src")
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
- cnx_files = list(src_dir.rglob("*.cnx"))
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
- for cnx_file in cnx_files:
437
- try:
438
- result = subprocess.run(
439
- ["cnext", str(cnx_file)],
440
- check=True,
441
- capture_output=True,
442
- text=True
443
- )
444
- print(f" \u2713 {cnx_file.name}")
445
- except subprocess.CalledProcessError as e:
446
- print(f" \u2717 Error: {cnx_file.name}")
447
- print(e.stderr)
448
- sys.exit(1)
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
- " 1. Create .cnx files in src/ (alongside your .c/.cpp files)"
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 automatically convert .cnx \u2192 .c before each build."
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
- return symbol?.kind === "variable" && symbol.type !== void 0;
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
- if (options.cppMode && options.userIncludes) {
133390
- for (const inc of options.userIncludes) {
133391
- const hVersion = inc.replace(/\.hpp"/, '.h"').replace(/\.hpp>/, ".h>");
133392
- userIncludeSet.add(hVersion);
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 (!userIncludeSet.has(directive)) {
133397
- lines.push(directive);
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: baseType,
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
- constructor(searchPaths, fs = defaultFs5) {
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 headerPath = includeInfo.path.replace(/\.cnx$|\.cnext$/, ".h");
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(searchPaths, this.fs);
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(searchPaths, this.fs);
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(