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