@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
147 lines • 6.71 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.generators = exports.searchLogger = void 0;
exports.getGenerator = getGenerator;
const flowr_search_1 = require("../flowr-search");
const parse_1 = require("../../slicing/criterion/parse");
const assert_1 = require("../../util/assert");
const query_1 = require("../../queries/query");
const search_enrichers_1 = require("./search-enrichers");
const log_1 = require("../../util/log");
const r_project_1 = require("../../r-bridge/lang-4.x/ast/model/nodes/r-project");
exports.searchLogger = log_1.log.getSubLogger({ name: 'search' });
/**
* All supported generators!
*/
exports.generators = {
all: generateAll,
get: generateGet,
criterion: generateCriterion,
from: generateFrom,
'from-query': generateFromQuery,
syntax: generateSyntax
};
async function generateAll(data) {
return new flowr_search_1.FlowrSearchElements((await getAllNodes(data))
.map(node => ({ node })));
}
async function getAllNodes(data) {
const normalize = await data.normalize();
return [...new Map([...normalize.idMap.values()].map(n => [n.info.id, n]))
.values()];
}
async function generateGet(input, { filter: { line, column, id, name, nameIsRegex, filePathRegex } }) {
const normalize = await input.normalize();
let potentials = (id ?
[normalize.idMap.get(id)].filter(assert_1.isNotUndefined) :
await getAllNodes(input));
if (filePathRegex) {
const filePathFilter = new RegExp(filePathRegex);
potentials = potentials.filter(({ info }) => info.file && filePathFilter.test(info.file));
}
if (line && line < 0) {
(0, assert_1.guard)(normalize.ast.files.length === 1, 'Currently, negative line numbers are only supported for single-file inputs');
const maxLines = normalize.ast.files[0].root.info.fullRange?.[2] ??
(id ? (await getAllNodes(input)) : potentials).reduce((maxLine, { location }) => location && location[2] > maxLine ? location[2] : maxLine, 0);
line = maxLines + line + 1;
}
if (line && column) {
potentials = potentials.filter(({ location }) => location?.[0] === line && location?.[1] === column);
}
else if (line) {
potentials = potentials.filter(({ location }) => location?.[0] === line);
}
else if (column) {
potentials = potentials.filter(({ location }) => location?.[1] === column);
}
if (nameIsRegex && name) {
const nameFilter = new RegExp(name);
potentials = potentials.filter(({ lexeme }) => lexeme && nameFilter.test(lexeme));
}
else if (name) {
potentials = potentials.filter(({ lexeme }) => lexeme === name);
}
return new flowr_search_1.FlowrSearchElements(potentials.map(node => ({ node })));
}
function generateFrom(_input, args) {
return new flowr_search_1.FlowrSearchElements(Array.isArray(args.from) ? args.from : [args.from]);
}
async function generateFromQuery(input, args) {
const result = await (0, query_1.executeQueries)({ analyzer: input }, args.from);
// collect involved nodes
const nodesByQuery = new Map();
for (const [query, content] of Object.entries(result)) {
if (query === '.meta') {
continue;
}
const nodes = new Set();
const queryDef = query_1.SupportedQueries[query];
for (const node of queryDef.flattenInvolvedNodes(content, args.from)) {
nodes.add({ node: (await input.normalize()).idMap.get(node) });
}
nodesByQuery.set(query, nodes);
}
// enrich elements with query data
const nodearr = [...nodesByQuery];
const elements = await new flowr_search_1.FlowrSearchElements(nodearr
.flatMap(([_, nodes]) => Array.from(nodes)))
.enrich(input, search_enrichers_1.Enrichment.QueryData, { queries: result });
return elements.mutate(s => Promise.all(s.map(async (e) => {
const [query, _] = nodearr.find(([_, nodes]) => nodes.has(e));
return await (0, search_enrichers_1.enrichElement)(e, elements, input, search_enrichers_1.Enrichment.QueryData, { query });
})));
}
async function generateSyntax(input, args) {
// if the user didn't specify a specific capture, we want to capture the outermost item
if (!args.captures?.length) {
(0, assert_1.guard)(typeof args.source === 'string', `Cannot use default capture name for pre-compiled query ${JSON.stringify(args.source)}, specify captures explicitly`);
const defaultCaptureName = 'defaultCapture';
args.source += ` @${defaultCaptureName}`;
args.captures = [defaultCaptureName];
}
// allow specifying capture names with or without the @ in front :)
const captures = new Set(args.captures.map(c => c.startsWith('@') ? c.substring(1) : c));
const info = input.parserInformation();
(0, assert_1.guard)(info.name === 'tree-sitter', 'treeSitterQuery can only be used with TreeSitterExecutor parsers!');
const result = await info.treeSitterQuery(args.source);
const relevant = result.filter(c => captures.has(c.name));
if (!relevant.length) {
exports.searchLogger.debug(`empty tree-sitter query result for query ${JSON.stringify(args)}`);
return new flowr_search_1.FlowrSearchElements([]);
}
const nodesByTreeSitterId = new Map();
r_project_1.RProject.visitAst((await input.normalize()).ast, node => {
const treeSitterInfo = node.info;
if (treeSitterInfo.tsId) {
nodesByTreeSitterId.set(treeSitterInfo.tsId, node);
}
else {
exports.searchLogger.debug(`normalized ast node ${node.lexeme} with type ${node.type} does not have a tree-sitter id`);
}
});
const ret = [];
for (const capture of relevant) {
const node = nodesByTreeSitterId.get(capture.node.id);
if (node) {
ret.push({ node });
}
else {
exports.searchLogger.debug(`tree-sitter node ${capture.node.id} with type ${capture.node.type} does not have a corresponding normalized ast node`);
}
}
return new flowr_search_1.FlowrSearchElements(ret);
}
async function generateCriterion(input, args) {
const idMap = (await input.normalize()).idMap;
return new flowr_search_1.FlowrSearchElements(args.criterion.map(c => ({ node: idMap.get(parse_1.SlicingCriterion.parse(c, idMap)) })));
}
/**
* Gets the search generator function for the given name
*/
function getGenerator(name) {
if (!exports.generators[name]) {
throw new Error(`Unknown generator: ${name}`);
}
return exports.generators[name];
}
//# sourceMappingURL=search-generators.js.map