All files / transpiler/output/codegen/assignment/handlers AccessPatternHandlers.ts

100% Statements 41/41
100% Branches 24/24
100% Functions 4/4
100% Lines 41/41

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 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169                                                      7x     7x 3x     7x 7x                 4x 1x     3x 3x                   7x 1x         6x 6x     6x 6x     6x   3x 3x 3x   3x 2x 1x         1x             1x                 3x   3x 2x 1x         1x     1x                                 14x   14x   3x 1x         2x 2x 2x   2x       11x 11x               14x                    
/**
 * Access pattern assignment handlers (ADR-109).
 *
 * Handles assignments with global/this prefix and member chains:
 * - GLOBAL_MEMBER: global.Counter.value <- 5
 * - GLOBAL_ARRAY: global.arr[i] <- value
 * - GLOBAL_REGISTER_BIT: global.GPIO7.DR_SET[bit] <- true
 * - THIS_MEMBER: this.count <- 5
 * - THIS_ARRAY: this.items[i] <- value
 * - MEMBER_CHAIN: struct.field.subfield <- value
 */
import AssignmentKind from "../AssignmentKind";
import IAssignmentContext from "../IAssignmentContext";
import IHandlerDeps from "./IHandlerDeps";
import BitUtils from "../../../../../utils/BitUtils";
import TAssignmentHandler from "./TAssignmentHandler";
import RegisterUtils from "./RegisterUtils";
 
/**
 * Common handler for global access patterns (GLOBAL_MEMBER and GLOBAL_ARRAY).
 *
 * Validates cross-scope visibility and generates standard assignment.
 */
function handleGlobalAccess(
  ctx: IAssignmentContext,
  deps: IHandlerDeps,
): string {
  const firstId = ctx.identifiers[0];
 
  // Validate cross-scope visibility if first id is a scope
  if (deps.isKnownScope(firstId) && ctx.identifiers.length >= 2) {
    deps.validateCrossScopeVisibility(firstId, ctx.identifiers[1]);
  }
 
  const target = deps.generateAssignmentTarget(ctx.targetCtx);
  return `${target} ${ctx.cOp} ${ctx.generatedValue};`;
}
 
/**
 * Common handler for this access patterns (THIS_MEMBER and THIS_ARRAY).
 *
 * Validates scope context and generates standard assignment.
 */
function handleThisAccess(ctx: IAssignmentContext, deps: IHandlerDeps): string {
  if (!deps.currentScope) {
    throw new Error("Error: 'this' can only be used inside a scope");
  }
 
  const target = deps.generateAssignmentTarget(ctx.targetCtx);
  return `${target} ${ctx.cOp} ${ctx.generatedValue};`;
}
 
/**
 * Handle global.reg[bit]: global.GPIO7.DR_SET[bit] <- true
 */
function handleGlobalRegisterBit(
  ctx: IAssignmentContext,
  deps: IHandlerDeps,
): string {
  if (ctx.isCompound) {
    throw new Error(
      `Compound assignment operators not supported for bit field access: ${ctx.cnextOp}`,
    );
  }
 
  const parts = ctx.identifiers;
  const regName = parts.join("_");
 
  // Check for write-only register
  const accessMod = deps.symbols.registerMemberAccess.get(regName);
  const isWriteOnly = RegisterUtils.isWriteOnlyRegister(accessMod);
 
  // Handle bit range vs single bit
  if (ctx.subscripts.length === 2) {
    // Bit range
    const start = deps.generateExpression(ctx.subscripts[0]);
    const width = deps.generateExpression(ctx.subscripts[1]);
    const mask = BitUtils.generateMask(width);
 
    if (isWriteOnly) {
      if (ctx.generatedValue === "0") {
        throw new Error(
          `Cannot assign 0 to write-only register bits ${regName}[${start}, ${width}]. ` +
            `Use the corresponding CLEAR register to clear bits.`,
        );
      }
      return RegisterUtils.generateWriteOnlyBitRange(
        regName,
        ctx.generatedValue,
        mask,
        start,
      );
    }
    return RegisterUtils.generateRmwBitRange(
      regName,
      ctx.generatedValue,
      mask,
      start,
    );
  }
 
  // Single bit
  const bitIndex = deps.generateExpression(ctx.subscripts[0]);
 
  if (isWriteOnly) {
    if (ctx.generatedValue === "false" || ctx.generatedValue === "0") {
      throw new Error(
        `Cannot assign false to write-only register bit ${regName}[${bitIndex}]. ` +
          `Use the corresponding CLEAR register to clear bits.`,
      );
    }
    return `${regName} = (1 << ${bitIndex});`;
  }
 
  return `${regName} = (${regName} & ~(1 << ${bitIndex})) | (${BitUtils.boolToInt(ctx.generatedValue)} << ${bitIndex});`;
}
 
/**
 * Handle member chain: struct.field.subfield <- value
 *
 * This is the catch-all for complex member access patterns
 * that don't match more specific handlers.
 *
 * Special case: Detects bit access at the end of chain
 * (e.g., grid[2][3].flags[0] <- true) and generates RMW.
 */
function handleMemberChain(
  ctx: IAssignmentContext,
  deps: IHandlerDeps,
): string {
  // Check if this is bit access on a struct member
  const bitAnalysis = deps.analyzeMemberChainForBitAccess(ctx.targetCtx);
 
  if (bitAnalysis.isBitAccess) {
    // Validate compound operators not supported for bit access
    if (ctx.isCompound) {
      throw new Error(
        `Compound assignment operators not supported for bit field access: ${ctx.cnextOp}`,
      );
    }
 
    const { baseTarget, bitIndex, baseType } = bitAnalysis;
    const one = BitUtils.oneForType(baseType!);
    const intValue = BitUtils.boolToInt(ctx.generatedValue.trim());
 
    return `${baseTarget} = (${baseTarget} & ~(${one} << ${bitIndex})) | (${intValue} << ${bitIndex});`;
  }
 
  // Normal member chain assignment
  const target = deps.generateAssignmentTarget(ctx.targetCtx);
  return `${target} ${ctx.cOp} ${ctx.generatedValue};`;
}
 
/**
 * All access pattern handlers for registration.
 */
const accessPatternHandlers: ReadonlyArray<
  [AssignmentKind, TAssignmentHandler]
> = [
  [AssignmentKind.GLOBAL_MEMBER, handleGlobalAccess],
  [AssignmentKind.GLOBAL_ARRAY, handleGlobalAccess],
  [AssignmentKind.GLOBAL_REGISTER_BIT, handleGlobalRegisterBit],
  [AssignmentKind.THIS_MEMBER, handleThisAccess],
  [AssignmentKind.THIS_ARRAY, handleThisAccess],
  [AssignmentKind.MEMBER_CHAIN, handleMemberChain],
];
 
export default accessPatternHandlers;