All files / TRANSPILE/3-Render/codegen/generators/declarationGenerators EnumGenerator.ts

100% Statements 15/15
100% Branches 2/2
100% Functions 1/1
100% Lines 14/14

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                                                                    47x             92x         92x 92x     92x 92x         92x   92x 221x   221x 221x 221x     91x     91x              
/**
 * EnumGenerator - ADR-017 Enum Declaration Generation
 *
 * Generates C typedef enum declarations from C-Next enum syntax.
 *
 * Example:
 *   enum State { IDLE, RUNNING, ERROR <- 255 }
 *   ->
 *   typedef enum {
 *       State_IDLE = 0,
 *       State_RUNNING = 1,
 *       State_ERROR = 255
 *   } State;
 */
import invariant from "../../../../../utils/invariant";
import IGeneratorInput from "../IGeneratorInput";
import IGeneratorState from "../IGeneratorState";
import IGeneratorOutput from "../IGeneratorOutput";
import IOrchestrator from "../IOrchestrator";
import TGeneratorFn from "../TGeneratorFn";
import QualifiedCName from "../../../../../utils/QualifiedCName";
import QualifiedNameGenerator from "../../../../../utils/QualifiedNameGenerator";
 
/**
 * Generate a C typedef enum from a C-Next enum declaration.
 *
 * ADR-017: Enums are strongly-typed with explicit integer backing.
 * Members are prefixed with the enum name to avoid C namespace collisions.
 *
 * #1445 box 3: takes the declared NAME, not the declaration node. The node was
 * read once, for `.IDENTIFIER().getText()`; the members and their values come
 * from `input.symbols.enumMembers`, which 1.3 Declare filled. Rendering an
 * enum never needed the tree -- it needed a name to look the enum up by.
 */
const generateEnum: TGeneratorFn<string> = (
  name: string,
  input: IGeneratorInput,
  state: IGeneratorState,
  _orchestrator: IOrchestrator,
): IGeneratorOutput => {
  // ADR-016: Apply scope prefix if inside a scope
  const fullName = QualifiedNameGenerator.forMember(
    state.currentScopePath,
    name,
  );
 
  const lines: string[] = [];
  lines.push(`typedef enum {`);
 
  // Look up enum members from symbols (collected by SymbolCollector)
  const members = input.symbols?.enumMembers.get(fullName);
  invariant(
    members,
    `every enum declaration codegen visits was collected by the resolver, so its qualified name is in enumMembers (missing '${fullName}')`,
  );
 
  const memberEntries = Array.from(members.entries());
 
  for (let i = 0; i < memberEntries.length; i++) {
    const [memberName, value] = memberEntries[i];
    // Prefix member names to avoid C namespace collisions
    const fullMemberName = QualifiedCName.fromParts([fullName, memberName]);
    const comma = i < memberEntries.length - 1 ? "," : "";
    lines.push(`    ${fullMemberName} = ${value}${comma}`);
  }
 
  lines.push(`} ${fullName};`, "");
 
  // No side effects needed for enum generation
  return {
    code: lines.join("\n"),
    effects: [],
  };
};
 
export default generateEnum;