UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

206 lines 9.07 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.mapDataFrameReplacementFunction = mapDataFrameReplacementFunction; const config_1 = require("../../../config"); const vertex_1 = require("../../../dataflow/graph/vertex"); const make_argument_1 = require("../../../dataflow/internal/process/functions/call/argument/make-argument"); const r_function_call_1 = require("../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call"); const type_1 = require("../../../r-bridge/lang-4.x/ast/model/type"); const resolve_args_1 = require("../resolve-args"); const semantics_1 = require("../semantics"); const access_mapper_1 = require("./access-mapper"); const arguments_1 = require("./arguments"); const identifier_1 = require("../../../dataflow/environments/identifier"); const built_in_proc_name_1 = require("../../../dataflow/environments/built-in-proc-name"); /** Mapper for mapping the supported data frame replacement functions to mapper functions */ const DataFrameReplacementFunctionMapper = { 'colnames': mapDataFrameColNamesAssignment, 'names': mapDataFrameColNamesAssignment, 'rownames': mapDataFrameRowNamesAssignment, 'dimnames': mapDataFrameDimNamesAssignment }; /** * Maps a concrete data frame replacement function call to abstract data frame operations. * @param node - The R node of the replacement function call * @param expression - The assigned expression node of the replacement function call * @param inference - The data frame shape inference visitor for checking data frame arguments * @param dfg - The data flow graph for resolving the arguments * @param ctx - The current flowR analysis context * @returns The mapped abstract data frame operations for the replacement function call, or `undefined` if the node does not represent a data frame replacement function call */ function mapDataFrameReplacementFunction(node, expression, inference, dfg, ctx) { const parent = hasParentReplacement(node, dfg) ? dfg.idMap?.get(node.info.parent) : undefined; const resolveInfo = { graph: dfg, idMap: dfg.idMap, full: true, resolve: config_1.VariableResolve.Alias, ctx }; if (node.type === type_1.RType.Access) { if (node.access.every(arg => arg === r_function_call_1.EmptyArgument)) { return mapDataFrameContentAssignment(node, expression, inference); } else if ((0, access_mapper_1.isStringBasedAccess)(node)) { return mapDataFrameNamedColumnAssignment(node, expression, inference, resolveInfo); } else { return mapDataFrameIndexColRowAssignment(node, expression, inference, resolveInfo); } } else if (node.type === type_1.RType.FunctionCall && node.named && node.arguments.length === 1 && node.arguments[0] !== r_function_call_1.EmptyArgument) { const n = identifier_1.Identifier.getName(node.functionName.content); if (isDataFrameReplacement(n)) { const mapper = DataFrameReplacementFunctionMapper[n]; return mapper(node.arguments[0], expression, inference, resolveInfo, parent); } else { return mapDataFrameUnknownAssignment(node.arguments[0], expression, inference); } } } function isDataFrameReplacement(functionName) { // a check with `functionName in DataFrameReplacementFunctionMapper` would return true for "toString" return Object.hasOwn(DataFrameReplacementFunctionMapper, functionName); } function hasParentReplacement(node, dfg) { const parentVertex = node.info.parent ? dfg.getVertex(node.info.parent) : undefined; return (0, vertex_1.isFunctionCallVertex)(parentVertex) && parentVertex.origin.includes(built_in_proc_name_1.BuiltInProcName.Replacement); } function mapDataFrameContentAssignment(access, expression, inference) { const dataFrame = access.accessed; if (!(0, arguments_1.isDataFrameArgument)(dataFrame, inference)) { return; } if ((0, arguments_1.isRNull)(expression)) { return [{ operation: 'subsetCols', operand: dataFrame.info.id, colnames: [], type: semantics_1.ConstraintType.OperandModification }]; } else { return [{ operation: 'identity', operand: dataFrame.info.id, type: semantics_1.ConstraintType.OperandModification }]; } } function mapDataFrameNamedColumnAssignment(access, expression, inference, info) { const dataFrame = access.accessed; if (!(0, arguments_1.isDataFrameArgument)(dataFrame, inference)) { return; } const colname = (0, resolve_args_1.resolveIdToArgValueSymbolName)(access.access[0], info); if ((0, arguments_1.isRNull)(expression)) { return [{ operation: 'removeCols', operand: dataFrame.info.id, colnames: colname ? [colname] : undefined, type: semantics_1.ConstraintType.OperandModification, options: { maybe: true } }]; } else { return [{ operation: 'assignCols', operand: dataFrame.info.id, columns: colname ? [colname] : undefined }]; } } function mapDataFrameIndexColRowAssignment(access, expression, inference, info) { const dataFrame = access.accessed; const args = access.access; if (!(0, arguments_1.isDataFrameArgument)(dataFrame, inference) || args.every(arg => arg === r_function_call_1.EmptyArgument)) { return; } const result = []; const rowArg = args.length < 2 ? undefined : args[0]; const colArg = args.length < 2 ? args[0] : args[1]; if (rowArg !== undefined && rowArg !== r_function_call_1.EmptyArgument) { const rowValue = (0, resolve_args_1.resolveIdToArgValue)(rowArg, info); let rows = undefined; if (typeof rowValue === 'number') { rows = [rowValue]; } else if (Array.isArray(rowValue) && rowValue.every(row => typeof row === 'number')) { rows = rowValue; } result.push({ operation: 'assignRows', operand: dataFrame.info.id, rows }); } if (colArg !== undefined && colArg !== r_function_call_1.EmptyArgument) { const colValue = (0, resolve_args_1.resolveIdToArgValue)(colArg, info); let columns = undefined; if (typeof colValue === 'string') { columns = [colValue]; } else if (typeof colValue === 'number') { columns = [colValue]; } else if (Array.isArray(colValue) && (colValue.every(col => typeof col === 'string') || colValue.every(col => typeof col === 'number'))) { columns = colValue; } if ((0, arguments_1.isRNull)(expression)) { result.push({ operation: 'removeCols', operand: dataFrame.info.id, colnames: columns?.map(col => typeof col === 'string' ? col : undefined), type: semantics_1.ConstraintType.OperandModification, options: { maybe: true } }); } else { result.push({ operation: 'assignCols', operand: dataFrame.info.id, columns }); } } return result; } function mapDataFrameColNamesAssignment(operand, expression, inference, info, parent) { if (!(0, arguments_1.isDataFrameArgument)(operand, inference)) { return; } const argument = info.idMap !== undefined ? (0, make_argument_1.toUnnamedArgument)(expression, info.idMap) : r_function_call_1.EmptyArgument; const assignedNames = (0, resolve_args_1.resolveIdToArgStringVector)(argument, info); return [{ operation: 'setColNames', operand: operand.value?.info.id, colnames: assignedNames, ...(parent !== undefined ? { options: { partial: true } } : {}) }]; } function mapDataFrameRowNamesAssignment(operand, _expression, inference) { if (!(0, arguments_1.isDataFrameArgument)(operand, inference)) { return; } return [{ operation: 'identity', operand: operand.value?.info.id, type: semantics_1.ConstraintType.OperandModification }]; } function mapDataFrameDimNamesAssignment(operand, _expression, inference) { if (!(0, arguments_1.isDataFrameArgument)(operand, inference)) { return; } return [{ operation: 'setColNames', operand: operand.value?.info.id, colnames: undefined }]; } function mapDataFrameUnknownAssignment(operand, _expression, inference) { if (!(0, arguments_1.isDataFrameArgument)(operand, inference)) { return; } return [{ operation: 'unknown', operand: operand.value?.info.id, type: semantics_1.ConstraintType.OperandModification }]; } //# sourceMappingURL=replacement-mapper.js.map