Dev Build / cleanup (push) Canceled after 0s
Dev Build / typecheck (push) Canceled after 0s
Dev Build / build (linux-arm64, ubuntu-24.04-arm, linux-arm64) (push) Canceled after 0s
Dev Build / build (linux-x64, ubuntu-latest, linux-x64) (push) Canceled after 0s
Dev Build / build (win-x64, windows-latest, win32-x64) (push) Canceled after 0s
Dev Build / dispatch-dev-docker (push) Canceled after 0s
146 lines
3.5 KiB
Plaintext
146 lines
3.5 KiB
Plaintext
const { compareRangeCovs } = require("./compare");
|
|
|
|
function emitForest(trees) {
|
|
return emitForestLines(trees).join("\n");
|
|
}
|
|
|
|
function emitForestLines(trees) {
|
|
const colMap = getColMap(trees);
|
|
const header = emitOffsets(colMap);
|
|
return [header, ...trees.map((tree) => emitTree(tree, colMap).join("\n"))];
|
|
}
|
|
|
|
function getColMap(trees) {
|
|
const eventSet = new Set();
|
|
for (const tree of trees) {
|
|
const stack = [tree];
|
|
while (stack.length > 0) {
|
|
const cur = stack.pop();
|
|
eventSet.add(cur.start);
|
|
eventSet.add(cur.end);
|
|
for (const child of cur.children) {
|
|
stack.push(child);
|
|
}
|
|
}
|
|
}
|
|
const events = [...eventSet];
|
|
events.sort((a, b) => a - b);
|
|
let maxDigits = 1;
|
|
for (const event of events) {
|
|
maxDigits = Math.max(maxDigits, event.toString(10).length);
|
|
}
|
|
const colWidth = maxDigits + 3;
|
|
const colMap = new Map();
|
|
for (const [i, event] of events.entries()) {
|
|
colMap.set(event, i * colWidth);
|
|
}
|
|
return colMap;
|
|
}
|
|
|
|
function emitTree(tree, colMap) {
|
|
const layers = [];
|
|
let nextLayer = [tree];
|
|
while (nextLayer.length > 0) {
|
|
const layer = nextLayer;
|
|
layers.push(layer);
|
|
nextLayer = [];
|
|
for (const node of layer) {
|
|
for (const child of node.children) {
|
|
nextLayer.push(child);
|
|
}
|
|
}
|
|
}
|
|
return layers.map((layer) => emitTreeLayer(layer, colMap));
|
|
}
|
|
|
|
function parseFunctionRanges(text, offsetMap) {
|
|
const result = [];
|
|
for (const line of text.split("\n")) {
|
|
for (const range of parseTreeLayer(line, offsetMap)) {
|
|
result.push(range);
|
|
}
|
|
}
|
|
result.sort(compareRangeCovs);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param layer Sorted list of disjoint trees.
|
|
* @param colMap
|
|
*/
|
|
function emitTreeLayer(layer, colMap) {
|
|
const line = [];
|
|
let curIdx = 0;
|
|
for (const { start, end, count } of layer) {
|
|
const startIdx = colMap.get(start);
|
|
const endIdx = colMap.get(end);
|
|
if (startIdx > curIdx) {
|
|
line.push(" ".repeat(startIdx - curIdx));
|
|
}
|
|
line.push(emitRange(count, endIdx - startIdx));
|
|
curIdx = endIdx;
|
|
}
|
|
return line.join("");
|
|
}
|
|
|
|
function parseTreeLayer(text, offsetMap) {
|
|
const result = [];
|
|
const regex = /\[(\d+)-*\)/gs;
|
|
while (true) {
|
|
const match = regex.exec(text);
|
|
if (match === null) {
|
|
break;
|
|
}
|
|
const startIdx = match.index;
|
|
const endIdx = startIdx + match[0].length;
|
|
const count = parseInt(match[1], 10);
|
|
const startOffset = offsetMap.get(startIdx);
|
|
const endOffset = offsetMap.get(endIdx);
|
|
if (startOffset === undefined || endOffset === undefined) {
|
|
throw new Error(`Invalid offsets for: ${JSON.stringify(text)}`);
|
|
}
|
|
result.push({ startOffset, endOffset, count });
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function emitRange(count, len) {
|
|
const rangeStart = `[${count.toString(10)}`;
|
|
const rangeEnd = ")";
|
|
const hyphensLen = len - (rangeStart.length + rangeEnd.length);
|
|
const hyphens = "-".repeat(Math.max(0, hyphensLen));
|
|
return `${rangeStart}${hyphens}${rangeEnd}`;
|
|
}
|
|
|
|
function emitOffsets(colMap) {
|
|
let line = "";
|
|
for (const [event, col] of colMap) {
|
|
if (line.length < col) {
|
|
line += " ".repeat(col - line.length);
|
|
}
|
|
line += event.toString(10);
|
|
}
|
|
return line;
|
|
}
|
|
|
|
function parseOffsets(text) {
|
|
const result = new Map();
|
|
const regex = /\d+/gs;
|
|
while (true) {
|
|
const match = regex.exec(text);
|
|
if (match === null) {
|
|
break;
|
|
}
|
|
result.set(match.index, parseInt(match[0], 10));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
module.exports = {
|
|
emitForest,
|
|
emitForestLines,
|
|
parseFunctionRanges,
|
|
parseOffsets,
|
|
};
|