All files / TRANSPILE/3-Render/codegen/helpers PostfixChainBuilder.ts

100% Statements 20/20
100% Branches 6/6
100% Functions 3/3
100% Lines 20/20

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                                                                258x 258x 258x   258x 345x 255x             255x   90x   345x     258x                         255x 255x 255x                           90x   77x 77x     13x   12x 12x       1x          
/**
 * Postfix Chain Builder
 *
 * Processes postfix operations (member access, array subscript, bit range)
 * in assignment target chains.
 *
 * Extracted from CodeGenerator.doGenerateAssignmentTarget to reduce
 * cognitive complexity.
 */
 
import IPostfixChainDeps from "../types/IPostfixChainDeps";
import IPostfixOperation from "../types/IPostfixOperation";
 
/**
 * Static utility for building postfix operation chains
 */
class PostfixChainBuilder {
  /**
   * Process a chain of postfix operations and build the result string
   *
   * @param baseResult The starting string (base identifier)
   * @param firstId The first identifier in the chain (for register detection)
   * @param operations Array of postfix operations to process
   * @param deps Dependencies for expression generation and separator resolution
   * @returns The complete result string with all postfix operations applied
   */
  static build(
    baseResult: string,
    firstId: string,
    operations: IPostfixOperation[],
    deps: IPostfixChainDeps,
  ): string {
    let result = baseResult;
    const identifierChain: string[] = [firstId];
    let isFirstOp = true;
 
    for (const op of operations) {
      if (op.memberName) {
        result = PostfixChainBuilder.processMemberAccess(
          result,
          op.memberName,
          identifierChain,
          isFirstOp,
          deps,
        );
        identifierChain.push(op.memberName);
      } else {
        result = PostfixChainBuilder.processSubscript(result, op);
      }
      isFirstOp = false;
    }
 
    return result;
  }
 
  /**
   * Process a member access operation (.identifier)
   */
  private static processMemberAccess(
    result: string,
    memberName: string,
    identifierChain: string[],
    isFirstOp: boolean,
    deps: IPostfixChainDeps,
  ): string {
    const chainWithMember = [...identifierChain, memberName];
    const separator = deps.getSeparator(isFirstOp, chainWithMember);
    return `${result}${separator}${memberName}`;
  }
 
  /**
   * Process a subscript operation ([expr] or [expr, expr])
   */
  private static processSubscript(
    result: string,
    op: IPostfixOperation,
  ): string {
    // #1652: the count decides, and only then does the render happen -- inside
    // the branch, never before it. Rendering an index queues a pending temp
    // declaration, so hoisting these calls above the `if` would queue one for
    // the shape that returns `result` unchanged.
    if (op.indexCount === 1) {
      // Single subscript: array access or single bit
      const [indexExpr] = op.renderIndexes();
      return `${result}[${indexExpr}]`;
    }
 
    if (op.indexCount === 2) {
      // Bit range: [start, width]
      const [start, width] = op.renderIndexes();
      return `${result}[${start}, ${width}]`;
    }
 
    // No expressions - shouldn't happen, but handle gracefully
    return result;
  }
}
 
export default PostfixChainBuilder;