@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
136 lines • 7.06 kB
JavaScript
;
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