UNPKG

@eagleoutice/flowr-dev

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

161 lines 7.32 kB
"use strict"; 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 range_1 = require("../../util/range"); 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(); // the id map holds some nodes under more than one id, so dedup; filled in place to skip the two // intermediate arrays a spread plus `map` would build const byId = new Map(); for (const n of normalize.idMap.values()) { byId.set(n.info.id, n); } return [...byId.values()]; } async function generateGet(input, { filter: { line, column, id, name, nameIsRegex, filePathRegex, fuzzy, innermostOnly } }) { 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 }) => 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 (fuzzy) { (0, assert_1.guard)(line, 'Fuzzy location matching requires line to be provided'); potentials = range_1.SourceRange.nodesContaining(potentials, line, column); if (innermostOnly) { potentials = range_1.SourceRange.innermostNodes(potentials); } } else 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 { idMap } = await input.normalize(); 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, args.certainty)) { nodes.add({ node: 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.slice(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