All files / utils QualifiedNameGenerator.ts

100% Statements 8/8
100% Branches 4/4
100% Functions 3/3
100% Lines 7/7

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                                                                  378x                                                         379x 375x 375x 374x   1x                                           492x          
/**
 * QualifiedNameGenerator - C-style name generation for C-Next symbols
 *
 * Provides transpiled C name generation for use in the output layer.
 * Delegates to ScopeUtils for the actual implementation to avoid duplication
 * with the types layer.
 *
 * Design decisions:
 * - Lives in output layer (codegen) since it generates C output
 * - Delegates to ScopeUtils for symbol-based name generation
 * - Provides string-based methods for backward compatibility
 * - Handles nested scopes: Outer.Inner.func -> Outer_Inner_func
 * - Global scope functions keep their bare names
 */
import type IProgram from "../types/IProgram";
import type IFunctionSymbol from "../types/symbols/IFunctionSymbol";
import ScopeUtils from "./ScopeUtils";
 
class QualifiedNameGenerator {
  // ============================================================================
  // Symbol-based methods (preferred)
  // ============================================================================
 
  /**
   * Generate the transpiled C name for a function.
   *
   * For global scope functions, returns the bare name (e.g., "main").
   * For scoped functions, returns "Scope_name" (e.g., "Test_fillData").
   * For nested scopes, returns "Outer_Inner_name" (e.g., "Outer_Inner_deepFunc").
   *
   * Delegates to ScopeUtils.getTranspiledCName() to avoid duplication.
   */
  static forFunction(func: IFunctionSymbol): string {
    return ScopeUtils.getTranspiledCName(func);
  }
 
  // #1298 removed `getScopePath`, a delegate to a `ScopeUtils` walk that no
  // longer exists: a scope's path is now a field on every symbol, so there is
  // nothing left to compute or to delegate.
 
  // ============================================================================
  // String-based methods (for transition - use symbol-based when possible)
  // ============================================================================
 
  /**
   * Generate a qualified function name for a function named inside a scope.
   *
   * Takes the scope PATH. #1285 replaced a leaf `string` with the scope symbol,
   * because the caller had to flatten a symbol to its leaf name and this had to
   * look it back up, losing any outer chain in between. #1298 makes it a string
   * again -- but the WHOLE path, which loses nothing, and which the caller already
   * holds.
   *
   * Falls back to qualifying the bare name when the function is not registered.
   */
  static forFunctionInScope(
    scopePath: string,
    funcName: string,
    program: IProgram | null,
  ): string {
    // #1452 box 3: the scope graph arrives as an argument rather than off a
    // global. `utils/` authors no facts and now reaches for none either.
    if (!program) return ScopeUtils.qualifyInScope(funcName, scopePath);
    const func = program.resolveFunction(funcName, scopePath);
    if (func) {
      return this.forFunction(func);
    }
    return ScopeUtils.qualifyInScope(funcName, scopePath);
  }
 
  /**
   * The C name a bare member of `scope` is emitted under. **The canonical
   * spelling for `output/`.**
   *
   * A one-line delegate to `ScopeUtils.qualifyInScope`, so there is one
   * implementation and no divergence to fix. What it settles is which of the two
   * public NAMES a codegen call site uses, because the two take their arguments in
   * opposite orders -- `forMember(scopePath, name)` against `qualifyInScope(name,
   * scopePath)`. Two spellings of one decision sixty lines apart in a file is how a
   * silently inverted call gets written by the next person editing nearby (#1357
   * review).
   *
   * `ScopeUtils.qualifyInScope` was the door for `logic/`, which could not import
   * `output/`, where this class lived (depcruise `logic-cannot-import-output`).
   * #1445 moved this class to `utils/` and #1444 dissolved `logic/` into 1.1
   * Discover, so the rule is retired. Both doors remain because both have
   * callers. This one delegates and decides nothing, so they cannot disagree.
   */
  static forMember(scopePath: string, memberName: string): string {
    return ScopeUtils.qualifyInScope(memberName, scopePath);
  }
}
 
export default QualifiedNameGenerator;