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 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 | 13x 13x 13x 13x 13x 275x 275x 275x 226x 49x 49x 21x 28x 28x 1x 27x 49x 49x 27x 22x 28x 28x 13x 15x 15x 12x 3x 3x 3x 1x 2x 27x 27x 7x 20x 20x 5x 15x 15x 2x 2x 2x 2x 13x 13x 2x 2x 2x 2x 11x 5x 5x 3x 2x 1x 1x 844x 712x 132x 132x 146x 146x 140x 140x 136x 132x 67x 67x 36x 31x 35x 31x 41x 9x 9x 9x 7x 26x 26x 20x 20x 20x 2x 18x 6x | /**
* ArrayDimensionParser - Utility for parsing array dimension expressions
*
* Issue #644: Extracted from CodeGenerator to consolidate array dimension parsing.
*
* This helper consolidates 4 different patterns for array dimension parsing:
* 1. _tryEvaluateConstant() - Full constant evaluation with const map
* 2. _evaluateArrayDimensions() - Parse all dimensions, drop unresolved
* 3. extractArrayDimensionsSimple() - Simple parseInt only
* 4. _setParameters() inline - Use 0 for unresolved dimensions
*/
import LiteralUtils from "../../../../utils/LiteralUtils.js";
import * as Parser from "../../../logic/parser/grammar/CNextParser.js";
/**
* Options for evaluating constant expressions.
*/
interface IConstantEvalOptions {
/** Map of const variable names to their numeric values */
constValues?: Map<string, number>;
/** Map of type names to their bit widths (for sizeof) */
typeWidths?: Record<string, number>;
/** Function to check if a type name is a known struct */
isKnownStruct?: (name: string) => boolean;
}
/**
* Helper class for parsing array dimension expressions.
*
* Supports various expression forms:
* - Integer literals (decimal, hex, binary)
* - Const variable references
* - sizeof(type) expressions
* - Binary expressions with const values (CONST + CONST)
*/
class ArrayDimensionParser {
/** Regex for identifier pattern */
private static readonly IDENTIFIER_RE = /^[a-zA-Z_]\w*$/;
/** Regex for const addition: CONST + CONST */
private static readonly CONST_ADD_RE = /^([a-zA-Z_]\w*)\+([a-zA-Z_]\w*)$/;
/** Regex for sizeof(type) */
private static readonly SIZEOF_RE = /^sizeof\(([a-zA-Z_]\w*)\)$/;
/** Regex for sizeof(type) * N */
private static readonly SIZEOF_MUL_RE = /^sizeof\(([a-zA-Z_]\w*)\)\*(\d+)$/;
/** Regex for sizeof(type) + N */
private static readonly SIZEOF_ADD_RE = /^sizeof\(([a-zA-Z_]\w*)\)\+(\d+)$/;
/**
* Parse a single expression as a compile-time constant.
*
* This is the most complete evaluation, supporting:
* - Integer literals (decimal, hex, binary)
* - Const variable references
* - sizeof(type) for primitive types
* - sizeof(type) * N and sizeof(type) + N
* - Binary expressions with const values (CONST + CONST)
*
* @param expr - The expression context to evaluate
* @param options - Optional evaluation options
* @returns The numeric value if constant, undefined if not evaluable
*/
static parseSingleDimension(
expr: Parser.ExpressionContext,
options?: IConstantEvalOptions,
): number | undefined {
const text = expr.getText().trim();
// Try integer literal first (most common case)
const literalValue = LiteralUtils.parseIntegerLiteral(text);
if (literalValue !== undefined) {
return literalValue;
}
// Try const identifier lookup
const constResult = this._tryResolveConstIdentifier(text, options);
if (constResult !== undefined) {
return constResult;
}
// Try const binary expression (CONST + CONST)
const binaryResult = this._tryEvaluateConstBinaryExpr(text, options);
if (binaryResult !== undefined) {
return binaryResult;
}
// Try sizeof expressions
return this._tryEvaluateSizeofExpr(text, options);
}
/**
* Try to resolve text as a const identifier.
*/
private static _tryResolveConstIdentifier(
text: string,
options?: IConstantEvalOptions,
): number | undefined {
const constValues = options?.constValues;
if (!constValues || !this.IDENTIFIER_RE.test(text)) {
return undefined;
}
return constValues.get(text);
}
/**
* Try to evaluate text as a const binary expression (CONST + CONST).
*/
private static _tryEvaluateConstBinaryExpr(
text: string,
options?: IConstantEvalOptions,
): number | undefined {
const constValues = options?.constValues;
if (!constValues) {
return undefined;
}
const match = this.CONST_ADD_RE.exec(text);
if (!match) {
return undefined;
}
const left = constValues.get(match[1]);
const right = constValues.get(match[2]);
if (left !== undefined && right !== undefined) {
return left + right;
}
return undefined;
}
/**
* Try to evaluate text as a sizeof expression.
* Handles: sizeof(type), sizeof(type) * N, sizeof(type) + N
*/
private static _tryEvaluateSizeofExpr(
text: string,
options?: IConstantEvalOptions,
): number | undefined {
const typeWidths = options?.typeWidths;
if (!typeWidths) {
return undefined;
}
// Try sizeof(type)
const sizeofMatch = this.SIZEOF_RE.exec(text);
if (sizeofMatch) {
return this._evaluateSimpleSizeof(
sizeofMatch[1],
typeWidths,
options?.isKnownStruct,
);
}
// Try sizeof(type) * N
const mulMatch = this.SIZEOF_MUL_RE.exec(text);
if (mulMatch) {
const bitWidth = typeWidths[mulMatch[1]];
const multiplier = Number.parseInt(mulMatch[2], 10);
Eif (bitWidth && !Number.isNaN(multiplier)) {
return (bitWidth / 8) * multiplier;
}
}
// Try sizeof(type) + N
const addMatch = this.SIZEOF_ADD_RE.exec(text);
if (addMatch) {
const bitWidth = typeWidths[addMatch[1]];
const addend = Number.parseInt(addMatch[2], 10);
Eif (bitWidth && !Number.isNaN(addend)) {
return bitWidth / 8 + addend;
}
}
return undefined;
}
/**
* Evaluate simple sizeof(type) expression.
*/
private static _evaluateSimpleSizeof(
typeName: string,
typeWidths: Record<string, number>,
isKnownStruct?: (name: string) => boolean,
): number | undefined {
const bitWidth = typeWidths[typeName];
if (bitWidth) {
return bitWidth / 8; // Convert bits to bytes
}
// Check if it's a known struct - can't compute size at this point
if (isKnownStruct?.(typeName)) {
return undefined;
}
return undefined;
}
/**
* Parse all array dimensions, dropping any that can't be resolved.
*
* Used for bitmap array registration and other contexts where
* unresolved dimensions should be skipped.
*
* @param arrayDims - The array dimension contexts to parse
* @param options - Optional evaluation options
* @returns Array of resolved dimension values, or undefined if none resolved
*/
static parseAllDimensions(
arrayDims: Parser.ArrayDimensionContext[] | null,
options?: IConstantEvalOptions,
): number[] | undefined {
if (!arrayDims || arrayDims.length === 0) {
return undefined;
}
const dimensions: number[] = [];
for (const dim of arrayDims) {
const sizeExpr = dim.expression();
if (sizeExpr) {
const size = ArrayDimensionParser.parseSingleDimension(
sizeExpr,
options,
);
if (size !== undefined && size > 0) {
dimensions.push(size);
}
}
}
return dimensions.length > 0 ? dimensions : undefined;
}
/**
* Iterate array dimensions with a callback for processing each.
* Handles null/empty array check centrally.
*/
private static forEachDimension(
arrayDims: Parser.ArrayDimensionContext[] | null,
processDim: (
sizeExpr: Parser.ExpressionContext | null,
dimensions: number[],
) => void,
): number[] {
const dimensions: number[] = [];
if (!arrayDims || arrayDims.length === 0) {
return dimensions;
}
for (const dim of arrayDims) {
processDim(dim.expression(), dimensions);
}
return dimensions;
}
/**
* Parse array dimensions using simple parseInt only.
*
* Used for contexts where only literal integers are expected
* (e.g., string array dimensions).
*
* @param arrayDims - The array dimension contexts to parse
* @returns Array of resolved dimension values (may be empty)
*/
static parseSimpleDimensions(
arrayDims: Parser.ArrayDimensionContext[] | null,
): number[] {
return ArrayDimensionParser.forEachDimension(
arrayDims,
(sizeExpr, dimensions) => {
Eif (sizeExpr) {
const size = Number.parseInt(sizeExpr.getText(), 10);
if (!Number.isNaN(size) && size > 0) {
dimensions.push(size);
}
}
},
);
}
/**
* Parse array dimensions for parameters, using 0 for unresolved sizes.
*
* Parameters need to track dimension count even when size is unknown
* (e.g., constant identifiers or unsized dimensions like arr[]).
*
* @param arrayDims - The array dimension contexts to parse
* @returns Array of dimension values (0 for unresolved/unsized)
*/
static parseForParameters(
arrayDims: Parser.ArrayDimensionContext[] | null,
): number[] {
return ArrayDimensionParser.forEachDimension(
arrayDims,
(sizeExpr, dimensions) => {
if (sizeExpr) {
const sizeText = sizeExpr.getText();
const size = Number.parseInt(sizeText, 10);
if (Number.isNaN(size)) {
// Non-numeric size (e.g., constant identifier) - still count the dimension
dimensions.push(0);
} else {
dimensions.push(size);
}
} else {
// Unsized dimension (e.g., arr[]) - use 0 to indicate unknown size
dimensions.push(0);
}
},
);
}
}
export default ArrayDimensionParser;
|