All files / lib parseCHeader.ts

100% Statements 24/24
100% Branches 15/15
100% Functions 3/3
100% Lines 23/23

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115                                  32x   4x   8x   2x   3x   8x   4x   3x                                                               24x   24x   24x 24x 24x 24x     24x 24x   24x 24x 24x     32x                     24x           3x             3x                  
/**
 * Parse C/C++ header source and extract symbols for IDE features
 */
 
import { CharStream, CommonTokenStream } from "antlr4ng";
import { CLexer } from "../transpiler/logic/parser/c/grammar/CLexer";
import { CParser } from "../transpiler/logic/parser/c/grammar/CParser";
import CSymbolCollector from "../transpiler/logic/symbols/CSymbolCollector";
import ESymbolKind from "../utils/types/ESymbolKind";
import ISymbolInfo from "./types/ISymbolInfo";
import IParseWithSymbolsResult from "./types/IParseWithSymbolsResult";
import TSymbolKind from "./types/TSymbolKind";
 
/**
 * Map ESymbolKind to TSymbolKind for extension use
 */
function mapSymbolKind(kind: ESymbolKind): TSymbolKind {
  switch (kind) {
    case ESymbolKind.Struct:
      return "struct";
    case ESymbolKind.Function:
      return "function";
    case ESymbolKind.Variable:
      return "variable";
    case ESymbolKind.Enum:
      return "enum";
    case ESymbolKind.EnumMember:
      return "enumMember";
    case ESymbolKind.Type:
      return "type";
    default:
      return "variable";
  }
}
 
/**
 * Parse C/C++ header source and extract symbols for IDE features
 *
 * Unlike transpilation, this function attempts to extract symbols even when
 * there are parse errors, making it suitable for autocomplete during typing.
 *
 * @param source - C/C++ header source code string
 * @param filePath - Optional file path for symbol source tracking
 * @returns Parse result with symbols
 *
 * @example
 * ```typescript
 * import parseCHeader from './lib/parseCHeader';
 *
 * const result = parseCHeader(headerSource, 'config.h');
 * // Find all functions defined in the header
 * const functions = result.symbols.filter(s => s.kind === 'function');
 * ```
 */
function parseCHeader(
  source: string,
  filePath?: string,
): IParseWithSymbolsResult {
  const errors: Array<{
    line: number;
    column: number;
    message: string;
    severity: "error" | "warning";
  }> = [];
 
  try {
    // Parse C source
    const charStream = CharStream.fromString(source);
    const lexer = new CLexer(charStream);
    const tokenStream = new CommonTokenStream(lexer);
    const parser = new CParser(tokenStream);
 
    // Suppress error output for headers (they often have incomplete code)
    lexer.removeErrorListeners();
    parser.removeErrorListeners();
 
    const tree = parser.compilationUnit();
    const collector = new CSymbolCollector(filePath ?? "<header>");
    const rawSymbols = collector.collect(tree);
 
    // Transform ISymbol[] to ISymbolInfo[]
    const symbols: ISymbolInfo[] = rawSymbols.map((sym) => ({
      name: sym.name,
      fullName: sym.parent ? `${sym.parent}.${sym.name}` : sym.name,
      kind: mapSymbolKind(sym.kind),
      type: sym.type,
      parent: sym.parent,
      line: sym.sourceLine ?? 0,
      sourceFile: filePath,
      language: "c",
    }));
 
    return {
      success: true,
      errors,
      symbols,
    };
  } catch (err) {
    errors.push({
      line: 1,
      column: 0,
      message: err instanceof Error ? err.message : String(err),
      severity: "error",
    });
 
    return {
      success: false,
      errors,
      symbols: [],
    };
  }
}
 
export default parseCHeader;