All files / transpiler/output/codegen/generators/expressions LiteralGenerator.ts

95.74% Statements 45/47
95.74% Branches 45/47
100% Functions 4/4
95.74% Lines 45/47

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                                              14x 14x                             769x 769x                 817x 12x     805x       805x 744x 34x       771x             769x                       14x           1249x 1249x     1249x 35x 35x         1214x 2x 2x 2x         2x               1212x 1x 1x   1211x 1x 1x             1210x 2x 2x   1208x 1x 1x   1207x   2x         1207x 1249x           769x 769x 6x 763x 682x       1207x        
/**
 * Literal Generator (ADR-053 A2)
 *
 * Generates C code for literal values:
 * - Boolean literals (true/false) → needs stdbool.h
 * - Float literals with C-Next suffixes (f32 → f, f64 → no suffix)
 * - Integer literals with C-Next suffixes (u64 → ULL, i64 → LL, strip 8/16/32)
 * - MISRA Rule 7.2: Unsigned suffix for unsigned integer types
 * - String and numeric literals pass through unchanged
 */
import { LiteralContext } from "../../../../logic/parser/grammar/CNextParser";
import IGeneratorOutput from "../IGeneratorOutput";
import TGeneratorEffect from "../TGeneratorEffect";
import IGeneratorInput from "../IGeneratorInput";
import IGeneratorState from "../IGeneratorState";
import IOrchestrator from "../IOrchestrator";
import NarrowingCastHelper from "../../helpers/NarrowingCastHelper.js";
import CodeGenState from "../../../../state/CodeGenState";
 
/**
 * Unsigned type patterns for MISRA Rule 7.2 compliance.
 * Includes both C-Next types (u8, u16, u32, u64) and C types (uint8_t, etc.)
 */
const UNSIGNED_64_TYPES = new Set(["u64", "uint64_t"]);
const UNSIGNED_TYPES = new Set([
  "u8",
  "u16",
  "u32",
  "uint8_t",
  "uint16_t",
  "uint32_t",
  "size_t", // Array indices (MISRA 7.2)
]);
 
/**
 * Resolve typedef aliases to their underlying type.
 * For C typedef'd types (e.g., "byte_t" -> "uint8_t"), look up the symbol table.
 */
function resolveTypedef(typeName: string): string {
  const underlyingType = CodeGenState.getTypedefType(typeName);
  return underlyingType ?? typeName;
}
 
/**
 * Check if a literal is a numeric integer (decimal, hex, octal, or binary).
 * Excludes strings, floats, and booleans.
 */
function isNumericIntegerLiteral(text: string): boolean {
  // Exclude strings (quoted)
  if (text.startsWith('"') || text.startsWith("'")) {
    return false;
  }
  // Exclude booleans
  Iif (text === "true" || text === "false") {
    return false;
  }
  // Exclude floats (contain decimal point or exponent without 0x prefix)
  if (!text.startsWith("0x") && !text.startsWith("0X")) {
    if (text.includes(".") || /[eE][+-]?\d/.test(text)) {
      return false;
    }
  }
  // Must start with digit or be hex/binary/octal
  return /^\d/.test(text) || /^0[xXbBoO]/.test(text);
}
 
/**
 * Check if a literal already has a C unsigned suffix (U, UL, ULL).
 */
function hasUnsignedSuffix(text: string): boolean {
  return /[uU]([lL]{0,2})$/.test(text);
}
 
/**
 * Generate C code for a literal value.
 *
 * @param node - The LiteralContext AST node
 * @param _input - Read-only context (unused for literals)
 * @param state - Current generation state (contains expectedType)
 * @param _orchestrator - For delegating to other generators (unused for literals)
 * @returns Generated code and effects (stdbool include for bool literals)
 */
const generateLiteral = (
  node: LiteralContext,
  _input: IGeneratorInput,
  state: IGeneratorState,
  _orchestrator: IOrchestrator,
): IGeneratorOutput => {
  const effects: TGeneratorEffect[] = [];
  let literalText = node.getText();
 
  // Track boolean literal usage to include stdbool.h
  if (literalText === "true" || literalText === "false") {
    effects.push({ type: "include", header: "stdbool" });
    return { code: literalText, effects };
  }
 
  // MISRA 10.3: Character literals have type int in C
  // When assigning to narrower types (u8, i8, u16, i16), add explicit cast
  if (literalText.startsWith("'")) {
    const expectedType = state?.expectedType;
    Eif (expectedType) {
      const wrappedCode = NarrowingCastHelper.wrap(
        literalText,
        "int",
        expectedType,
      );
      return { code: wrappedCode, effects };
    }
    return { code: literalText, effects };
  }
 
  // ADR-024: Transform C-Next float suffixes to standard C syntax
  // 3.14f32 -> 3.14f (C float)
  // 3.14f64 -> 3.14 (C double, no suffix needed)
  if (/[fF]32$/.test(literalText)) {
    literalText = literalText.replace(/[fF]32$/, "f");
    return { code: literalText, effects };
  }
  if (/[fF]64$/.test(literalText)) {
    literalText = literalText.replace(/[fF]64$/, "");
    return { code: literalText, effects };
  }
 
  // Issue #130: Transform C-Next integer suffixes to standard C syntax
  // u8/u16/u32 and i8/i16/i32 suffixes are stripped (C infers from context)
  // u64 -> ULL suffix for 64-bit unsigned
  // i64 -> LL suffix for 64-bit signed
  if (/[uU]64$/.test(literalText)) {
    literalText = literalText.replace(/[uU]64$/, "ULL");
    return { code: literalText, effects };
  }
  if (/[iI]64$/.test(literalText)) {
    literalText = literalText.replace(/[iI]64$/, "LL");
    return { code: literalText, effects };
  }
  if (/[uUiI](8|16|32)$/.test(literalText)) {
    // Strip 8/16/32-bit suffixes - will add U below if needed for MISRA
    literalText = literalText.replace(/[uUiI](8|16|32)$/, "");
  }
 
  // MISRA Rule 7.2: Add unsigned suffix based on expectedType
  // Only applies to numeric integer literals without existing unsigned suffix
  const expectedType = state?.expectedType;
  if (
    expectedType &&
    isNumericIntegerLiteral(literalText) &&
    !hasUnsignedSuffix(literalText)
  ) {
    // Resolve typedef aliases (e.g., "byte_t" -> "uint8_t")
    const resolvedType = resolveTypedef(expectedType);
    if (UNSIGNED_64_TYPES.has(resolvedType)) {
      literalText = literalText + "ULL";
    } else if (UNSIGNED_TYPES.has(resolvedType)) {
      literalText = literalText + "U";
    }
  }
 
  return { code: literalText, effects };
};
 
export default generateLiteral;