@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
111 lines • 4.85 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CriteriaParseError = exports.SlicingCriteria = exports.SlicingCriterion = void 0;
const log_1 = require("../../util/log");
const node_id_1 = require("../../r-bridge/lang-4.x/ast/model/processing/node-id");
const static_slicer_1 = require("../static/static-slicer");
const type_1 = require("../../r-bridge/lang-4.x/ast/model/type");
/**
* The helper object associated with {@link SlicingCriterion} which makes it easy
* to parse, validate and resolve slicing criteria.
*/
exports.SlicingCriterion = {
name: 'SlicingCriterion',
/**
* Takes a criterion in the form of `line:column` or `line@variable-name` and returns the corresponding node id
* @see {@link SlicingCriterion.tryParse} for a version that does not throw an error
*/
parse(criterion, idMap) {
const resolved = exports.SlicingCriterion.tryParse(criterion, idMap);
if (resolved === undefined) {
throw new CriteriaParseError(`invalid slicing criterion ${criterion}`);
}
return resolved;
},
/**
* Tries to resolve a slicing criterion to an id, but does not throw an error if it fails.
* @see {@link SlicingCriterion.parse} for the version that throws an error
*/
tryParse(criterion, idMap) {
criterion = criterion.toString(); // in case it's a number
if (criterion.startsWith('$')) {
return node_id_1.NodeId.normalize(criterion.substring(1));
}
else if (criterion.includes('@')) {
const at = criterion.indexOf('@');
const line = parseInt(criterion.substring(0, at));
const name = criterion.substring(at + 1);
return conventionalCriteriaToId(line, name, idMap);
}
else if (criterion.includes(':')) {
const [line, column] = criterion.split(':').map(c => parseInt(c));
return locationToId([line, column], idMap);
}
},
/**
* Converts a node id to a slicing criterion in the form of `$id`
*/
fromId(id) {
return `$${id}`;
}
};
/**
* The helper object associated with {@link SlicingCriteria} which makes it easy to parse, validate and resolve slicing criteria.
*/
exports.SlicingCriteria = {
/**
* Decodes all slicing criteria to their corresponding node ids
* @throws CriteriaParseError if any of the criteria can not be resolved
* @see {@link SlicingCriteria.convertAll}
*/
decodeAll(criteria, decorated) {
return criteria.map(l => ({ criterion: l, id: exports.SlicingCriterion.parse(l, decorated) }));
},
/**
* Converts all criteria to their id in the AST if possible, this keeps the original criterion if it can not be resolved.
* @see {@link SlicingCriteria.decodeAll}
*/
convertAll(criteria, decorated) {
return criteria.map(l => exports.SlicingCriterion.tryParse(l, decorated) ?? l);
}
};
/**
* Thrown if the given slicing criteria can not be found
*/
class CriteriaParseError extends Error {
constructor(message) {
super(message);
this.name = 'CriteriaParseError';
}
}
exports.CriteriaParseError = CriteriaParseError;
function locationToId(location, dataflowIdMap) {
let candidate;
for (const [id, nodeInfo] of dataflowIdMap.entries()) {
if (nodeInfo.location === undefined || nodeInfo.location[0] !== location[0] || nodeInfo.location[1] !== location[1]) {
continue; // only consider those with position information
}
(0, log_1.expensiveTrace)(static_slicer_1.slicerLogger, () => `can resolve id ${id} (${JSON.stringify(nodeInfo.location)}) for location ${JSON.stringify(location)}`);
// function calls have the same location as the symbol they refer to, so we need to prefer the function call
if (candidate !== undefined && nodeInfo.type !== type_1.RType.FunctionCall || nodeInfo.type === type_1.RType.Argument || nodeInfo.type === type_1.RType.ExpressionList) {
continue;
}
candidate = nodeInfo;
}
return candidate?.info.id;
}
function conventionalCriteriaToId(line, name, dataflowIdMap) {
let candidate;
for (const nodeInfo of dataflowIdMap.values()) {
if (nodeInfo.location === undefined || nodeInfo.location[0] !== line || nodeInfo.lexeme !== name) {
continue;
}
// function calls have the same location as the symbol they refer to, so we need to prefer the function call
if (candidate !== undefined && nodeInfo.type !== type_1.RType.FunctionCall || nodeInfo.type === type_1.RType.Argument || nodeInfo.type === type_1.RType.ExpressionList) {
continue;
}
candidate = nodeInfo;
}
return candidate?.info.id;
}
//# sourceMappingURL=parse.js.map