UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

163 lines 7.33 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.mapDataFrameAccess = mapDataFrameAccess; exports.isStringBasedAccess = isStringBasedAccess; const config_1 = require("../../../config"); 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 arguments_1 = require("./arguments"); /** * Special named arguments of index-based access operators */ const SpecialAccessArgumentsMapper = { '[': ['drop'], '[[': ['exact'] }; /** * Maps a concrete data frame access operation to abstract data frame operations. * @param node - The R node of the access * @param inference - The data frame shape inference visitor * @param dfg - The data flow graph for resolving the arguments * @param ctx - The current flowR analyzer context * @returns The mapped abstract data frame operations for the access operation, or `undefined` if the node does not represent a data frame access operation */ function mapDataFrameAccess(node, inference, dfg, ctx) { if (node.type !== type_1.RType.Access) { return; } const resolveInfo = { graph: dfg, idMap: dfg.idMap, full: true, resolve: config_1.VariableResolve.Alias, ctx }; if (isStringBasedAccess(node)) { return mapDataFrameNamedColumnAccess(node, inference, resolveInfo); } else { return mapDataFrameIndexColRowAccess(node, inference, resolveInfo); } } function mapDataFrameNamedColumnAccess(access, 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); return [{ operation: 'accessCols', operand: dataFrame.info.id, columns: colname ? [colname] : undefined }]; } function mapDataFrameIndexColRowAccess(access, inference, info) { const dataFrame = access.accessed; const drop = (0, arguments_1.getArgumentValue)(access.access, 'drop', info); const exact = (0, arguments_1.getArgumentValue)(access.access, 'exact', info); const args = getAccessArgs(access.operator, access.access); if (!(0, arguments_1.isDataFrameArgument)(dataFrame, inference)) { return; } else if (args.every(arg => arg === r_function_call_1.EmptyArgument)) { return [{ operation: 'identity', operand: dataFrame.info.id }]; } const result = []; const rowArg = args.length < 2 ? undefined : args[0]; const colArg = args.length < 2 ? args[0] : args[1]; let rows = undefined; let columns = undefined; if (rowArg !== undefined && rowArg !== r_function_call_1.EmptyArgument) { const rowValue = (0, resolve_args_1.resolveIdToArgValue)(rowArg, info); if (typeof rowValue === 'number') { rows = [rowValue]; } else if (Array.isArray(rowValue) && rowValue.every(row => typeof row === 'number')) { rows = rowValue; } result.push({ operation: 'accessRows', operand: dataFrame.info.id, rows: rows?.map(Math.abs) }); } if (colArg !== undefined && colArg !== r_function_call_1.EmptyArgument) { const colValue = (0, resolve_args_1.resolveIdToArgValue)(colArg, info); if (typeof colValue === 'number') { columns = [colValue]; } else if (typeof colValue === 'string' && exact !== false) { columns = [colValue]; } else if (Array.isArray(colValue) && colValue.every(col => typeof col === 'number')) { columns = colValue; } else if (Array.isArray(colValue) && colValue.every(col => typeof col === 'string') && exact !== false) { columns = colValue; } result.push({ operation: 'accessCols', operand: dataFrame.info.id, columns: columns?.every(col => typeof col === 'number') ? columns.map(Math.abs) : columns }); } // The data frame extent is dropped if the operator `[[` is used, the argument `drop` is true, or only one column is accessed const dropExtent = access.operator === '[[' ? true : args.length === 2 && typeof drop === 'boolean' ? drop : rowArg !== undefined && columns?.length === 1 && (typeof columns[0] === 'string' || columns[0] > 0); if (!dropExtent) { const rowSubset = rows === undefined || rows.every(row => row >= 0); const colSubset = columns === undefined || columns.every(col => typeof col === 'string' || col >= 0); const rowZero = rows?.length === 1 && rows[0] === 0; const colZero = columns?.length === 1 && columns[0] === 0; const duplicateRows = rows?.some((row, index, list) => list.indexOf(row) !== index); const duplicateCols = columns?.some((col, index, list) => list.indexOf(col) !== index); let operand = dataFrame; if (rowArg !== undefined && rowArg !== r_function_call_1.EmptyArgument) { if (rowSubset) { result.push({ operation: 'subsetRows', operand: operand?.info.id, rows: rowZero ? 0 : rows?.filter(index => index !== 0).length, ...(duplicateRows ? { options: { duplicateRows: true } } : {}) }); } else { result.push({ operation: 'removeRows', operand: operand?.info.id, rows: rowZero ? 0 : rows?.filter(index => index !== 0).length }); } operand = undefined; } if (colArg !== undefined && colArg !== r_function_call_1.EmptyArgument) { if (colSubset) { result.push({ operation: 'subsetCols', operand: operand?.info.id, colnames: colZero ? [] : columns?.map(col => typeof col === 'string' ? col : undefined), ...(duplicateCols ? { options: { duplicateCols: true } } : {}) }); } else { result.push({ operation: 'removeCols', operand: operand?.info.id, colnames: columns?.map(col => typeof col === 'string' ? col : undefined) }); } operand = undefined; } } return result; } /** * Removes all special named arguments from the arguments of an access operator (i.e. arguments like "drop" and "exact"). */ function getAccessArgs(operator, args) { const specialArgs = SpecialAccessArgumentsMapper[operator]; return args.filter(arg => arg === r_function_call_1.EmptyArgument || arg.name === undefined || !specialArgs.includes((0, resolve_args_1.unquoteArgument)(arg.name.content))); } /** * Checks whether an access node represents a string-based access (`$` or `@`), and no index-based access (`[` or `[[`). */ function isStringBasedAccess(access) { return access.operator === '$' || access.operator === '@'; } //# sourceMappingURL=access-mapper.js.map