@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
122 lines • 4.65 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.dataAccess = void 0;
const common_syntax_probability_1 = require("../../common-syntax-probability");
const post_process_1 = require("./post-process");
const assert_1 = require("../../../../util/assert");
const type_1 = require("../../../../r-bridge/lang-4.x/ast/model/type");
const role_1 = require("../../../../r-bridge/lang-4.x/ast/model/processing/role");
const statistics_file_1 = require("../../../output/statistics-file");
const r_function_call_1 = require("../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
const r_project_1 = require("../../../../r-bridge/lang-4.x/ast/model/nodes/r-project");
const model_1 = require("../../../../r-bridge/lang-4.x/ast/model/model");
const initialDataAccessInfo = {
// for the nth argument, how many of them are constant, etc.
singleBracket: {
// only counts if empty
0: 0n,
1: (0, common_syntax_probability_1.emptyCommonSyntaxTypeCounts)()
},
doubleBracket: {
// similar to single bracket
0: 0n,
1: (0, common_syntax_probability_1.emptyCommonSyntaxTypeCounts)()
},
chainedOrNestedAccess: 0,
longestChain: 0,
deepestNesting: 0,
byName: 0,
bySlot: 0,
};
function classifyArguments(args, existing) {
if (args.length === 0) {
existing[0]++;
return;
}
let i = 1;
for (const arg of args) {
if (arg === null || arg === undefined || arg === r_function_call_1.EmptyArgument) {
existing[0]++;
continue;
}
existing[i] = (0, common_syntax_probability_1.updateCommonSyntaxTypeCounts)(existing[i] ?? (0, common_syntax_probability_1.emptyCommonSyntaxTypeCounts)(), arg);
i++;
}
}
function visitAccess(info, input) {
const accessNest = [];
const accessChain = [];
const parentRoleCache = new Map();
r_project_1.RProject.visitAst(input.normalizedRAst.ast, node => {
if (node.type !== type_1.RType.Access) {
return;
}
const roles = (0, role_1.rolesOfParents)(node, input.normalizedRAst.idMap);
let acc = false;
let idxAcc = false;
for (const role of roles) {
if (role === "acc" /* RoleInParent.Accessed */) {
acc = true;
break; // we only account for the first one
}
else if (role === "idx-acc" /* RoleInParent.IndexAccess */) {
idxAcc = true;
break;
}
}
// here we have to check after the addition as we can only check the parental context
if (acc) {
accessChain.push(node);
info.chainedOrNestedAccess++;
info.longestChain = Math.max(info.longestChain, accessChain.length);
}
else if (idxAcc) {
accessNest.push(node);
info.chainedOrNestedAccess++;
info.deepestNesting = Math.max(info.deepestNesting, accessNest.length);
}
parentRoleCache.set(node.info.id, { acc, idxAcc });
if (accessNest.length === 0 && accessChain.length === 0) { // store topmost, after add as it must not be a child to do that
(0, statistics_file_1.appendStatisticsFile)(exports.dataAccess.name, 'dataAccess', [model_1.RNode.lexeme(node)], input.filepath);
}
const op = node.operator;
switch (op) {
case '@':
info.bySlot++;
return;
case '$':
info.byName++;
return;
case '[':
classifyArguments(node.access, info.singleBracket);
break;
case '[[':
classifyArguments(node.access, info.doubleBracket);
break;
default: (0, assert_1.assertUnreachable)(op);
}
(0, assert_1.guard)(Array.isArray(node.access), '[ and [[ must provide access as array');
}, node => {
// drop again :D
if (node.type === type_1.RType.Access) {
const ctx = parentRoleCache.get(node.info.id);
if (ctx?.acc) {
accessChain.pop();
}
else if (ctx?.idxAcc) {
accessNest.pop();
}
}
});
}
exports.dataAccess = {
name: 'Data Access',
description: 'Ways of accessing data structures in R',
process(existing, input) {
visitAccess(existing, input);
return existing;
},
initialValue: initialDataAccessInfo,
postProcess: post_process_1.postProcess
};
//# sourceMappingURL=data-access.js.map