UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

136 lines 7.06 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.processPipe = processPipe; const known_call_handling_1 = require("../known-call-handling"); const assert_1 = require("../../../../../../util/assert"); const unpack_argument_1 = require("../argument/unpack-argument"); const model_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/model"); const logger_1 = require("../../../../../logger"); const type_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/type"); const vertex_1 = require("../../../../../graph/vertex"); const edge_1 = require("../../../../../graph/edge"); const identifier_1 = require("../../../../../environments/identifier"); const make_argument_1 = require("../argument/make-argument"); const built_in_assignment_1 = require("./built-in-assignment"); const built_in_proc_name_1 = require("../../../../../environments/built-in-proc-name"); const log_1 = require("../../../../../../util/log"); /** * Support for R's pipe functions like `|>` or magrittr's `%>%` */ function processPipe(name, args, rootId, data, { pipePlaceholderName, assignLhs, returnLhs, rhsMightBeSymbol = false }) { const fCallInfo = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, origin: built_in_proc_name_1.BuiltInProcName.Pipe }); const processedArguments = fCallInfo.processedArguments; let information = fCallInfo.information; if (args.length !== 2) { logger_1.dataflowLogger.warn(`Pipe ${identifier_1.Identifier.toString(name.content)} has something else than 2 arguments, skipping`); return information; } const [lhs, rhs] = args.map(e => (0, unpack_argument_1.unpackNonameArg)(e)); (0, assert_1.guard)(lhs !== undefined && rhs !== undefined, () => `lhs and rhs must be present, but ${JSON.stringify(lhs)} and ${JSON.stringify(rhs)} were found instead.`); // If this is an assigning pipe (e.g., %<>%), perform the assignment writeback using the built-in // assignment processor: target <- source where target is the original lhs and source is the rhs call if (assignLhs) { // create unnamed args for target and source const targetArg = (0, make_argument_1.toUnnamedArgument)(lhs, data.completeAst.idMap); const sourceArg = (0, make_argument_1.toUnnamedArgument)(rhs, data.completeAst.idMap); // construct a synthetic symbol for the assignment operator '<-' const assignSym = { type: type_1.RType.Symbol, info: name.info, content: identifier_1.Identifier.make('<-', 'base'), lexeme: '<-', location: name.location }; information = (0, built_in_assignment_1.processAssignment)(assignSym, [targetArg, sourceArg], rootId, data, { canBeReplacement: true, mayHaveMoreArgs: true }); } let treatedAsFunctionCall = false; if (rhs.type === type_1.RType.Symbol && rhsMightBeSymbol) { // convert a plain symbol on the RHS into a function-call vertex so we can treat it like `df %>% head` const maybeVertex = information.graph.getVertex(rhs.info.id); if (maybeVertex && maybeVertex.tag === vertex_1.VertexType.Use) { information.graph.updateToFunctionCall({ tag: vertex_1.VertexType.FunctionCall, id: rhs.info.id, name: rhs.content, args: [], environment: data.environment, onlyBuiltin: false, cds: data.cds, origin: [built_in_proc_name_1.BuiltInProcName.Function] }); treatedAsFunctionCall = true; } } if (treatedAsFunctionCall || rhs.type === type_1.RType.FunctionCall) { const functionCallNode = information.graph.getVertex(rhs.info.id); (0, assert_1.guard)(functionCallNode?.tag === vertex_1.VertexType.FunctionCall, () => `Expected function call node with id ${rhs.info.id} to be a function call node, but got ${functionCallNode?.tag} instead.`); // make the lhs an argument node (or link it to placeholders within the rhs call): const argId = lhs.info.id; // find all symbol occurrences inside the rhs function call AST that match the placeholder name const occurrenceIds = []; model_1.RNode.visitAst(rhs, (node) => { if (node.type === type_1.RType.Symbol && node.content === pipePlaceholderName) { occurrenceIds.push(node.info.id); } return false; }); if (occurrenceIds.length > 0) { if (occurrenceIds.length !== 1) { log_1.log.warn(`Expected exactly one occurrence of the pipe placeholder '${identifier_1.Identifier.toString(pipePlaceholderName)}' in the rhs of the pipe, but found ${occurrenceIds.length}. Linking all occurrences to the lhs.`); } for (const occId of occurrenceIds) { information.graph.addEdge(occId, argId, edge_1.EdgeType.Reads); } } else { logger_1.dataflowLogger.trace(`Linking pipe arg ${argId} as first argument of ${rhs.info.id}`); functionCallNode.args.unshift({ name: undefined, nodeId: argId, cds: data.cds, type: identifier_1.ReferenceType.Function }); information.graph.addEdge(functionCallNode.id, argId, edge_1.EdgeType.Argument | edge_1.EdgeType.Reads); } } else { logger_1.dataflowLogger.warn(`Expected rhs of pipe to be a function call, but got ${rhs.type} instead.`); } const firstArgument = processedArguments[0]; // If requested, return the lhs value (tee/TPipe semantics): add a Returns edge to the lhs entry if (firstArgument && returnLhs) { information.graph.addEdge(rootId, firstArgument.entryPoint, edge_1.EdgeType.Returns); } else { const secondArgument = processedArguments[1]; if (secondArgument && !returnLhs) { information.graph.addEdge(rootId, secondArgument.entryPoint, edge_1.EdgeType.Returns); } } const uniqueIn = information.in.slice(); for (const ing of (firstArgument?.in ?? [])) { if (!uniqueIn.some(e => e.nodeId === ing.nodeId)) { uniqueIn.push(ing); } } const uniqueOut = information.out.slice(); for (const outg of (firstArgument?.out ?? [])) { if (!uniqueOut.some(e => e.nodeId === outg.nodeId)) { uniqueOut.push(outg); } } const uniqueUnknownReferences = information.unknownReferences.slice(); for (const unknown of (firstArgument?.unknownReferences ?? [])) { if (!uniqueUnknownReferences.some(e => e.nodeId === unknown.nodeId)) { uniqueUnknownReferences.push(unknown); } } return { ...information, in: uniqueIn, out: uniqueOut, unknownReferences: uniqueUnknownReferences, entryPoint: rootId }; } //# sourceMappingURL=built-in-pipe.js.map