UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

163 lines 6.35 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FlowrFilterCombinator = exports.FlowrFilters = exports.ValidFlowrFiltersReverse = exports.ValidFlowrFilters = exports.FlowrFilter = void 0; exports.testFunctionsIgnoringPackage = testFunctionsIgnoringPackage; exports.binaryTreeToString = binaryTreeToString; exports.isBinaryTree = isBinaryTree; exports.evalFilter = evalFilter; const type_1 = require("../r-bridge/lang-4.x/ast/model/type"); const vertex_1 = require("../dataflow/graph/vertex"); const search_enrichers_1 = require("./search-executor/search-enrichers"); var FlowrFilter; (function (FlowrFilter) { /** * Drops search elements that represent empty arguments. Specifically, all nodes that are arguments and have an undefined name are skipped. * This filter does not accept any arguments. */ FlowrFilter["DropEmptyArguments"] = "drop-empty-arguments"; /** * Only returns search elements whose enrichments' JSON representations match a given test regular expression. * This filter accepts {@link MatchesEnrichmentArgs}, which includes the enrichment to match for, as well as the regular expression to test the enrichment's (non-pretty-printed) JSON representation for. * To test for included function names in an enrichment like {@link Enrichment.CallTargets}, the helper function {@link testFunctionsIgnoringPackage} can be used. */ FlowrFilter["MatchesEnrichment"] = "matches-enrichment"; })(FlowrFilter || (exports.FlowrFilter = FlowrFilter = {})); exports.ValidFlowrFilters = new Set(Object.values(FlowrFilter)); exports.ValidFlowrFiltersReverse = Object.fromEntries(Object.entries(FlowrFilter).map(([k, v]) => [v, k])); exports.FlowrFilters = { [FlowrFilter.DropEmptyArguments]: ((e, _args) => { return e.node.type !== type_1.RType.Argument || e.node.name !== undefined; }), [FlowrFilter.MatchesEnrichment]: ((e, args) => { const content = JSON.stringify((0, search_enrichers_1.enrichmentContent)(e, args.enrichment)); return content !== undefined && args.test.test(content); }) }; function testFunctionsIgnoringPackage(functions) { return new RegExp(`"(.+:::?)?(${functions.join('|')})"`); } /** * @see {@link FlowrFilterCombinator.is} * @see {@link evalFilter} * @see {@link binaryTreeToString} */ class FlowrFilterCombinator { tree; constructor(init) { this.tree = this.unpack(init); } static is(value) { if (typeof value === 'string' && exports.ValidFlowrFilters.has(value)) { return new this({ type: 'special', value: value }); } else if (typeof value === 'object') { const name = value?.name; if (name && exports.ValidFlowrFilters.has(name)) { return new this({ type: 'special', value: value }); } else { return new this(value); } } else if (type_1.ValidRTypes.has(value)) { return new this({ type: 'r-type', value: value }); } else if (vertex_1.ValidVertexTypes.has(value)) { return new this({ type: 'vertex-type', value: value }); } else { throw new Error(`Invalid filter value: ${value}`); } } and(right) { return this.binaryRight('and', right); } or(right) { return this.binaryRight('or', right); } xor(right) { return this.binaryRight('xor', right); } binaryRight(op, right) { this.tree = { type: op, left: this.tree, right: this.unpack(FlowrFilterCombinator.is(right)) }; return this; } not() { return this.unary('not'); } unary(op) { this.tree = { type: op, operand: this.tree }; return this; } unpack(val) { return val instanceof FlowrFilterCombinator ? val.tree : val; } get() { return this.tree; } } exports.FlowrFilterCombinator = FlowrFilterCombinator; function binaryTreeToString(tree) { const res = treeToStringImpl(tree, 0); // drop outer parens if (res.startsWith('(') && res.endsWith(')')) { return res.slice(1, -1); } else { return res; } } const typeToSymbol = { 'and': '∧', 'or': '∨', 'xor': '⊕', 'not': '¬' }; function treeToStringImpl(tree, depth) { if (tree.type === 'r-type' || tree.type === 'vertex-type' || tree.type === 'special') { return typeof tree.value === 'string' ? tree.value : `${tree.value.name}@${JSON.stringify(tree.value.args)}`; } if (tree.type === 'not') { return `${typeToSymbol[tree.type]}${treeToStringImpl(tree.operand, depth)}`; } const left = treeToStringImpl(tree.left, depth + 1); const right = treeToStringImpl(tree.right, depth + 1); return `(${left} ${typeToSymbol[tree.type]} ${right})`; } function isBinaryTree(tree) { return typeof tree === 'object' && tree !== null && 'tree' in tree; } const evalVisit = { and: ({ left, right }, data) => evalTree(left, data) && evalTree(right, data), or: ({ left, right }, data) => evalTree(left, data) || evalTree(right, data), xor: ({ left, right }, data) => evalTree(left, data) !== evalTree(right, data), not: ({ operand }, data) => !evalTree(operand, data), 'r-type': ({ value }, { element }) => element.node.type === value, 'vertex-type': ({ value }, { dataflow: { graph }, element }) => graph.getVertex(element.node.info.id)?.tag === value, 'special': ({ value }, { element }) => { const name = typeof value === 'string' ? value : value.name; const args = typeof value === 'string' ? undefined : value.args; const getHandler = exports.FlowrFilters[name]; if (getHandler) { return getHandler(element, args); } throw new Error(`Couldn't find special filter with name ${name}`); } }; function evalTree(tree, data) { /* we ensure that the types fit */ return evalVisit[tree.type](tree, data); } function evalFilter(filter, data) { /* common lift, this can be improved easily :D */ const tree = FlowrFilterCombinator.is(filter); return evalTree(tree.get(), data); } //# sourceMappingURL=flowr-search-filters.js.map