UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

147 lines 6.71 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 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