All files / PARSE/3-Declare/cnext/collectors RegisterCollector.ts

100% Statements 16/16
100% Branches 2/2
100% Functions 1/1
100% Lines 16/16

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                                                                          80x 80x       80x     80x   80x 80x   80x 118x 118x 118x     118x         118x                                     118x   118x                               118x     80x                                          
/**
 * RegisterCollector - Extracts register block declarations from parse trees.
 * Registers provide typed access to memory-mapped I/O locations.
 *
 * Produces TType-based IRegisterSymbol with proper IScopeSymbol references.
 */
 
import * as Parser from "../../../2-Parse/grammar/CNextParser";
import ESourceLanguage from "../../../../utils/types/ESourceLanguage";
import IRegisterSymbol from "../../../../types/symbols/IRegisterSymbol";
import type IRegisterMemberSymbol from "../../../../types/symbols/IRegisterMemberSymbol";
import TypeUtils from "../utils/TypeUtils";
import ScopeUtils from "../../../../utils/ScopeUtils";
import TVisibility from "../../../../types/TVisibility";
import ParserUtils from "../../../../utils/ParserUtils";
import MemberSymbolBase from "../utils/MemberSymbolBase";
import type TAccessMode from "../../../../types/TRegisterAccessMode";
 
class RegisterCollector {
  /**
   * Collect a register declaration and return an IRegisterSymbol.
   *
   * @param ctx The register declaration context
   * @param sourceFile Source file path
   * @param knownBitmaps Set of known bitmap type names for reference resolution
   * @param scopePath The path of the scope this register belongs to (dotted path, "" at file scope)
   * @param isScopeType ADR-057 predicate: is this *qualified* name a scope type?
   * @returns The register symbol with proper scope reference
   */
  static collect(
    ctx: Parser.RegisterDeclarationContext,
    sourceFile: string,
    knownBitmaps: Set<string>,
    scopePath: string,
    visibility: TVisibility,
    isScopeType?: (qualifiedName: string) => boolean,
  ): IRegisterSymbol {
    const name = ctx.IDENTIFIER().getText();
    const span = ParserUtils.getSpan(ctx);
    // #1298: members carry the scope's PATH, not the scope object. The path
    // holds every outer component, so nothing downstream can flatten it to a
    // leaf -- which is what the reference threaded here used to protect against.
    const baseAddress = ctx.expression().getText();
 
    // Collect register members
    const members = new Map<string, IRegisterMemberSymbol>();
    // #1318: a member hangs off the REGISTER, not the enclosing scope.
    const identity = ScopeUtils.identityOf({ name, scopePath });
    const ownerScopedName = identity.cnxScopedName;
 
    for (const member of ctx.registerMember()) {
      const memberName = member.IDENTIFIER().getText();
      const offset = member.expression().getText();
      const accessMod = member.accessModifier().getText() as TAccessMode;
 
      // Get member type and convert to C type
      const typeName = TypeUtils.getTypeName(
        member.type(),
        scopePath,
        isScopeType,
      );
      const cType = TypeUtils.cnextTypeToCType(typeName);
 
      // Is the member type a bitmap? ONE key, the one the ladder produced.
      //
      // ADR-057: "Qualify from the parse tree, never from a resolved name."
      // `typeName` has already been through the single TypeBinding ladder
      // above, which qualified it if and only if the syntax and the scope
      // called for it. Re-qualifying it here was a SECOND qualification
      // decision taken on an already-resolved name -- and by that point
      // `global.Flags` and a bare `Flags` are byte-identical, so probing the
      // re-qualified key FIRST let a scope-local `Chip__Flags` capture a
      // deliberate `global.Flags`. The transpiler exited 0 and emitted
      // `volatile Chip__Flags*`, whose bit names differ, so the field access
      // was never lowered and gcc rejected the generated C.
      //
      // One key is sufficient because `knownBitmaps` is keyed exactly as the
      // ladder resolves: file-scope bitmaps by their bare `name`, scope
      // bitmaps by `fullyQualifiedCName`. The two shapes are complementary,
      // never alternatives for the same declaration.
      const bitmapType = knownBitmaps.has(typeName) ? typeName : undefined;
 
      const memberInfo: IRegisterMemberSymbol = {
        ...MemberSymbolBase.of({
          kind: "register_member" as const,
          name: memberName,
          parentScopedName: ownerScopedName,
          memberCtx: member,
          parentSpan: span,
          sourceFile,
          visibility,
        }),
        offset,
        cType,
        access: accessMod,
        bitmapType,
      };
 
      members.set(memberName, memberInfo);
    }
 
    return {
      kind: "register",
      name,
      scopePath,
      // #1285: identity computed once, from the scope chain, not
      // re-derived by every consumer.
      // #1318 review: the same identity the members were keyed by, not a
      // second call with the same arguments -- change one and the members
      // would keep the old parent name while this reported the new one.
      ...identity,
      sourceFile,
      span,
      sourceLanguage: ESourceLanguage.CNext,
      visibility,
      baseAddress,
      members,
    };
  }
}
 
export default RegisterCollector;