UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

275 lines 9.19 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SourceLocation = exports.SourceRange = exports.SourcePosition = void 0; const assert_1 = require("./assert"); /** * Utility functions for {@link SourcePosition|source positions}. */ exports.SourcePosition = { name: 'SourcePosition', /** * Formats a {@link SourcePosition|source position} as a human-readable string. */ format(pos) { if (pos === undefined) { return '??.??'; } else { return `${pos[0]}.${pos[1]}`; } }, /** * Creates a source position from the given line and column numbers. * @param line - line number (starts with 1) * @param column - column number (starts with 1) */ from(line, column) { return [Number(line), Number(column)]; }, /** * returns an invalid source position */ invalid() { return [-1, -1]; }, /** * Returns the line of a source position */ getLine(pos) { return pos[0]; }, /** * Returns the column of a source position */ getColumn(pos) { return pos[1]; }, }; /** * Utility functions for {@link SourceRange|source ranges}. */ exports.SourceRange = { name: 'SourceRange', /** * Prints a {@link SourceRange|range} as a human-readable string. */ format(range) { if (range === undefined) { return '??-??'; } else if (range[0] === range[2]) { if (range[1] === range[3]) { return `${range[0]}.${range[1]}`; } else { return `${range[0]}.${range[1]}-${range[3]}`; } } return `${range[0]}.${range[1]}-${range[2]}.${range[3]}`; }, /** * Returns the start position of a source range. */ getStart(range) { return [range[0], range[1]]; }, /** * Returns the start line of a source range. */ getStartLine(range) { return range[0]; }, /** * Returns the end position of a source range. */ getEnd(range) { return [range[2], range[3]]; }, /** * Returns the end line of a source range. */ getEndLine(range) { return range[2]; }, /** * Creates a source range from the given line and column numbers. * @param sl - start line * @param sc - start column * @param el - end line * @param ec - end column */ from(sl, sc, el = sl, ec = sc) { return [Number(sl), Number(sc), Number(el), Number(ec)]; }, /** * returns an invalid source range */ invalid() { return [-1, -1, -1, -1]; }, /** * Merges multiple source ranges into a single source range that spans from the earliest start to the latest end. * If you are interested in combining overlapping ranges into a minimal set of ranges, see {@link combineRanges}. * @throws if no ranges are provided */ merge(rs) { const rsSafe = rs.filter(assert_1.isNotUndefined); (0, assert_1.guard)(rsSafe.length > 0, 'Cannot merge no ranges'); return rsSafe.reduce(([sl, sc, el, ec], [nsl, nsc, nel, nec]) => [ ...(sl < nsl || (sl === nsl && sc < nsc) ? [sl, sc] : [nsl, nsc]), ...(el > nel || (el === nel && ec > nec) ? [el, ec] : [nel, nec]) ], rsSafe[0]); }, /** * @returns true iff `r1` starts and ends before `r2` starts (i.e., if `r1` and `r2` do not overlap and `r1` comes before `r2` */ startsCompletelyBefore([, , r1el, r1ec], [r2sl, r2sc, ,]) { return r1el < r2sl || (r1el === r2sl && r1ec < r2sc); }, /** * Checks if the two ranges overlap. */ overlap([r1sl, r1sc, r1el, r1ec], [r2sl, r2sc, r2el, r2ec]) { return r1sl <= r2el && r2sl <= r1el && r1sc <= r2ec && r2sc <= r1ec; }, /** * Calculates the component-wise sum of two ranges. */ add([r1sl, r1sc, r1el, r1ec], [r2sl, r2sc, r2el, r2ec]) { return [r1sl + r2sl, r1sc + r2sc, r1el + r2el, r1ec + r2ec]; }, /** * Provides a comparator for {@link SourceRange}s that sorts them in ascending order. * @returns a positive number if `r1` comes after `r2`, a negative number if `r1` comes before `r2`, and `0` if they are equal */ compare([r1sl, r1sc, ,], [r2sl, r2sc, ,]) { if (r1sl === r2sl) { return r1sc - r2sc; } else { return r1sl - r2sl; } }, /** * Checks if the first range is a subset of the second range. */ isSubsetOf([r1sl, r1sc, r1el, r1ec], [r2sl, r2sc, r2el, r2ec]) { return (r1sl > r2sl || r1sl === r2sl && r1sc >= r2sc) && (r1el < r2el || r1sl === r2sl && r1ec <= r2ec); }, /** * Combines overlapping or subset ranges into a minimal set of ranges. * @see {@link SourceRange.merge} for merging multiple ranges into a single range. */ combineRanges(...ranges) { return ranges.filter(range => !ranges.some(other => range !== other && exports.SourceRange.isSubsetOf(range, other))); }, fromNode(node) { return node?.info.fullRange ?? node?.location; } }; /** * Utility functions for {@link SourceLocation|source locations}. */ exports.SourceLocation = { name: 'SourceLocation', /** * Formats a {@link SourceLocation|source location} as a human-readable string. */ format(location) { if (location === undefined) { return '??:??-??'; } else if (location[4] !== undefined) { return `${location[4]}:${exports.SourceRange.format(location)}`; } else { return exports.SourceRange.format(location); } }, /** * Returns the {@link SourceRange|source range} part of a {@link SourceLocation|source location}. */ getRange(location) { return location; }, getFile(location) { return location[4]; }, /** * Returns the file part of a {@link SourceLocation|source location}, or `undefined` if no file is set. */ from(range, file) { return file !== undefined ? [...range, file] : range; }, /** * Creates a {@link SourceLocation|source location} from a {@link SourceRange|source range} and a file name. * @returns undefined if the given range is undefined * @see {@link SourceRange.fromNode} for getting the range from an AST node */ fromNode(node) { const range = exports.SourceRange.fromNode(node); return range !== undefined ? exports.SourceLocation.from(range, node.info.file) : undefined; }, /** * Maps the file part of a {@link SourceLocation|source location} using the given mapper function. */ mapFile(loc, fileMapper) { const range = exports.SourceLocation.getRange(loc); const file = loc[4]; return exports.SourceLocation.from(range, fileMapper(file)); }, /** * Checks if the first location is a subset of the second location. * For this, they must be in the same file! * @see {@link SourceRange.isSubsetOf} */ isSubsetOf(loc1, loc2) { if (exports.SourceLocation.getFile(loc1) !== exports.SourceLocation.getFile(loc2)) { return false; } return exports.SourceRange.isSubsetOf(exports.SourceLocation.getRange(loc1), exports.SourceLocation.getRange(loc2)); }, compare(loc1, loc2) { const res = exports.SourceRange.compare(exports.SourceLocation.getRange(loc1), exports.SourceLocation.getRange(loc2)); if (res !== 0) { return res; } const file1 = exports.SourceLocation.getFile(loc1); const file2 = exports.SourceLocation.getFile(loc2); if (file1 === file2) { return 0; } else if (file1 === undefined) { return -1; } else if (file2 === undefined) { return 1; } else { return file1 < file2 ? -1 : 1; } }, /** * Returns an invalid source location (i.e., with an invalid range and no file). */ invalid() { return exports.SourceRange.invalid(); }, /** * Merges multiple source locations into a single source location that spans from the earliest start to the latest end. * If the locations are from different files, `undefined` is returned. * Files may be `undefined` themselves, but if there is at least one defined file, they must all be the same defined file for the merge to succeed. */ merge(locs) { const locsSafe = locs.filter(assert_1.isNotUndefined); if (locsSafe.length === 0) { return undefined; } const firstFile = locsSafe.find(loc => loc[4] !== undefined)?.[4]; if (locsSafe.some(loc => loc[4] !== undefined && loc[4] !== firstFile)) { return undefined; } return exports.SourceLocation.from(exports.SourceRange.merge(locsSafe.map(exports.SourceLocation.getRange)), firstFile); } }; //# sourceMappingURL=range.js.map