UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

111 lines 4.85 kB
"use strict"; 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