UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

380 lines 15 kB
"use strict"; /** * The decoration module is tasked with taking an R-ast given by a {@link RNode} and * * 1. assigning a unique id to each node (see {@link IdGenerator}) * 2. transforming the AST into a doubly linked tree using the ids (so it stays serializable) * * The main entry point is {@link decorateAst}. * * @module */ Object.defineProperty(exports, "__esModule", { value: true }); exports.deterministicCountingIdGenerator = deterministicCountingIdGenerator; exports.deterministicPrefixIdGenerator = deterministicPrefixIdGenerator; exports.sourcedDeterministicCountingIdGenerator = sourcedDeterministicCountingIdGenerator; exports.nodeToLocationId = nodeToLocationId; exports.deterministicLocationIdGenerator = deterministicLocationIdGenerator; exports.decorateAst = decorateAst; const assert_1 = require("../../../../../util/assert"); const bimap_1 = require("../../../../../util/bimap"); const type_1 = require("../type"); const stateful_fold_1 = require("./stateful-fold"); const r_function_call_1 = require("../nodes/r-function-call"); /** * The simplest id generator which just increments a number on each call. */ function deterministicCountingIdGenerator(id = 0) { return () => id++; } function deterministicPrefixIdGenerator(prefix, id = 0) { return () => `${prefix}-${id++}`; } function sourcedDeterministicCountingIdGenerator(path, location, start = 0) { let id = start; return () => `${path}-${loc2Id(location)}-${id++}`; } function loc2Id([sl, sc, el, ec]) { return `${sl}:${sc}-${el}:${ec}`; } /** * Generates the location id, used by {@link deterministicLocationIdGenerator}. * * @param data - the node to generate an id for, must have location information */ function nodeToLocationId(data) { const loc = data.location; (0, assert_1.guard)(loc !== undefined, 'location must be defined to generate a location id'); return loc2Id(loc); } /** * Generates unique ids based on the locations of the node (see {@link nodeToLocationId}). * If a node has no location information, it will be assigned a unique counter-value. * * @param start - the start value for the counter, in case nodes do not have location information */ function deterministicLocationIdGenerator(start = 0) { let id = start; return (data) => data.location !== undefined ? nodeToLocationId(data) : `${id++}`; } const defaultParentContext = { role: "root" /* RoleInParent.Root */, index: 0 }; const nestForElement = new Set([ type_1.RType.FunctionDefinition, type_1.RType.ForLoop, type_1.RType.WhileLoop, type_1.RType.RepeatLoop, type_1.RType.IfThenElse, ]); /** * Covert the given AST into a doubly linked tree while assigning ids (so it stays serializable). * * @param ast - The root of the AST to convert * @param getId - The id generator: must generate a unique id für each passed node * @param file - the path to the file this AST was extracted from will be added to the nodes * * @typeParam OtherInfo - The original decoration of the ast nodes (probably is nothing as the id decoration is most likely the first step to be performed after extraction) * * @returns A decorated AST based on the input and the id provider. */ function decorateAst(ast, { getId = deterministicCountingIdGenerator(0), file }) { const idMap = new bimap_1.BiMap(); const info = { idMap, getId, file }; /* Please note, that all fold processors do not re-create copies in higher-folding steps so that the idMap stays intact. */ const foldLeaf = createFoldForLeaf(info); const foldBinaryOp = createFoldForBinaryOp(info); const unaryOp = createFoldForUnaryOp(info); /* we pass down the nesting depth */ const decoratedAst = (0, stateful_fold_1.foldAstStateful)(ast, 0, { down: (n, nesting) => { if (nestForElement.has(n.type)) { return nesting + 1; } else { return nesting; } }, foldNumber: foldLeaf, foldString: foldLeaf, foldLogical: foldLeaf, foldSymbol: foldLeaf, foldAccess: createFoldForAccess(info), foldBinaryOp: foldBinaryOp, foldPipe: foldBinaryOp, foldUnaryOp: unaryOp, other: { foldComment: foldLeaf, foldLineDirective: foldLeaf }, loop: { foldFor: createFoldForForLoop(info), foldRepeat: createFoldForRepeatLoop(info), foldWhile: createFoldForWhileLoop(info), foldBreak: foldLeaf, foldNext: foldLeaf }, foldIfThenElse: createFoldForIfThenElse(info), foldExprList: createFoldForExprList(info), functions: { foldFunctionDefinition: createFoldForFunctionDefinition(info), foldFunctionCall: createFoldForFunctionCall(info), foldArgument: createFoldForFunctionArgument(info), foldParameter: createFoldForFunctionParameter(info) } }); decoratedAst.info.role = "root" /* RoleInParent.Root */; decoratedAst.info.index = 0; return { ast: decoratedAst, idMap }; } function createFoldForLeaf(info) { return (data, nesting) => { const id = info.getId(data); const decorated = { ...data, info: { ...data.info, id, parent: undefined, ...defaultParentContext, nesting } }; decorated.info.file = info.file; info.idMap.set(id, decorated); return decorated; }; } function createFoldForBinaryOp(info) { return (data, lhs, rhs, nesting) => { const id = info.getId(data); const decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, lhs, rhs }; info.idMap.set(id, decorated); const lhsInfo = lhs.info; lhsInfo.parent = id; const rhsInfo = rhs.info; rhsInfo.parent = id; rhsInfo.index = 1; if (data.type === type_1.RType.Pipe) { lhsInfo.role = "pipe-lhs" /* RoleInParent.PipeLhs */; rhsInfo.role = "pipe-rhs" /* RoleInParent.PipeRhs */; } else { lhsInfo.role = "binop-lhs" /* RoleInParent.BinaryOperationLhs */; rhsInfo.role = "binop-rhs" /* RoleInParent.BinaryOperationRhs */; } decorated.info.file = info.file; return decorated; }; } function createFoldForUnaryOp(info) { return (data, operand, nesting) => { const id = info.getId(data); const decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, operand }; info.idMap.set(id, decorated); const opInfo = operand.info; opInfo.parent = id; opInfo.role = "unary-operand" /* RoleInParent.UnaryOperand */; decorated.info.file = info.file; return decorated; }; } function createFoldForAccess(info) { return (data, accessed, access, nesting) => { const id = info.getId(data); const decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, accessed, access }; info.idMap.set(id, decorated); const accessedInfo = accessed.info; accessedInfo.parent = id; accessedInfo.role = "accessed" /* RoleInParent.Accessed */; if (typeof access !== 'string') { let idx = 0; // the first oe will be skipped in the first iter for (const acc of access) { idx++; if (acc !== r_function_call_1.EmptyArgument) { const curInfo = acc.info; curInfo.parent = id; curInfo.index = idx; curInfo.role = "index-access" /* RoleInParent.IndexAccess */; } } } decorated.info.file = info.file; return decorated; }; } function createFoldForForLoop(info) { return (data, variable, vector, body, nesting) => { const id = info.getId(data); const decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, variable, vector, body }; info.idMap.set(id, decorated); const varInfo = variable.info; varInfo.parent = id; varInfo.role = "for-variable" /* RoleInParent.ForVariable */; const vecInfo = vector.info; vecInfo.parent = id; vecInfo.index = 1; vecInfo.role = "for-vector" /* RoleInParent.ForVector */; const bodyInfo = body.info; bodyInfo.parent = id; bodyInfo.index = 2; bodyInfo.role = "for-body" /* RoleInParent.ForBody */; decorated.info.file = info.file; return decorated; }; } function createFoldForRepeatLoop(info) { return (data, body, nesting) => { const id = info.getId(data); const decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, body }; info.idMap.set(id, decorated); const bodyInfo = body.info; bodyInfo.parent = id; bodyInfo.role = "repeat-body" /* RoleInParent.RepeatBody */; decorated.info.file = info.file; return decorated; }; } function createFoldForWhileLoop(info) { return (data, condition, body, nesting) => { const id = info.getId(data); const decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, condition, body }; info.idMap.set(id, decorated); const condInfo = condition.info; condInfo.parent = id; condInfo.role = "while-cond" /* RoleInParent.WhileCondition */; const bodyInfo = body.info; bodyInfo.parent = id; bodyInfo.index = 1; bodyInfo.role = "while-body" /* RoleInParent.WhileBody */; decorated.info.file = info.file; return decorated; }; } function createFoldForIfThenElse(info) { return (data, condition, then, otherwise, nesting) => { const id = info.getId(data); const decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, condition, then, otherwise }; info.idMap.set(id, decorated); const condInfo = condition.info; condInfo.parent = id; condInfo.role = "if-cond" /* RoleInParent.IfCondition */; const thenInfo = then.info; thenInfo.parent = id; thenInfo.index = 1; thenInfo.role = "if-then" /* RoleInParent.IfThen */; if (otherwise) { const otherwiseInfo = otherwise.info; otherwiseInfo.parent = id; otherwiseInfo.index = 2; otherwiseInfo.role = "if-otherwise" /* RoleInParent.IfOtherwise */; } decorated.info.file = info.file; return decorated; }; } function createFoldForExprList(info) { return (data, grouping, children, nesting) => { const id = info.getId(data); const decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, grouping, children }; info.idMap.set(id, decorated); let i = 0; for (const child of children) { const childInfo = child.info; childInfo.parent = id; childInfo.index = i++; childInfo.role = "expr-list-child" /* RoleInParent.ExpressionListChild */; } decorated.info.file = info.file; return decorated; }; } function createFoldForFunctionCall(info) { return (data, functionName, args, nesting) => { const id = info.getId(data); let decorated; if (data.named) { decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, functionName, arguments: args }; } else { decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, calledFunction: functionName, arguments: args }; } info.idMap.set(id, decorated); const funcInfo = functionName.info; funcInfo.parent = id; funcInfo.role = "call-name" /* RoleInParent.FunctionCallName */; let idx = 0; for (const arg of args) { idx++; if (arg !== r_function_call_1.EmptyArgument) { const argInfo = arg.info; argInfo.parent = id; argInfo.index = idx; argInfo.role = "call-argument" /* RoleInParent.FunctionCallArgument */; } } decorated.info.file = info.file; return decorated; }; } function createFoldForFunctionDefinition(info) { return (data, params, body, nesting) => { const id = info.getId(data); const decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, parameters: params, body }; info.idMap.set(id, decorated); let idx = 0; for (const param of params) { const paramInfo = param.info; paramInfo.parent = id; paramInfo.index = idx++; paramInfo.role = "function-def-param" /* RoleInParent.FunctionDefinitionParameter */; } const bodyInfo = body.info; bodyInfo.parent = id; bodyInfo.index = idx; bodyInfo.role = "function-def-body" /* RoleInParent.FunctionDefinitionBody */; decorated.info.file = info.file; return decorated; }; } function createFoldForFunctionParameter(info) { return (data, name, defaultValue, nesting) => { const id = info.getId(data); const decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, name, defaultValue }; info.idMap.set(id, decorated); const nameInfo = name.info; nameInfo.parent = id; nameInfo.role = "param-name" /* RoleInParent.ParameterName */; if (defaultValue) { const defaultInfo = defaultValue.info; defaultInfo.parent = id; defaultInfo.index = 1; defaultInfo.role = "param-value" /* RoleInParent.ParameterDefaultValue */; } decorated.info.file = info.file; return decorated; }; } function createFoldForFunctionArgument(info) { return (data, name, value, nesting) => { const id = info.getId(data); const decorated = { ...data, info: { ...data.info, id, parent: undefined, nesting }, name, value }; info.idMap.set(id, decorated); let idx = 0; if (name) { const nameInfo = name.info; nameInfo.parent = id; nameInfo.role = "arg-name" /* RoleInParent.ArgumentName */; idx++; // adaptive, 0 for the value if there is no name! } if (value) { const valueInfo = value.info; valueInfo.parent = id; valueInfo.index = idx; valueInfo.role = "arg-value" /* RoleInParent.ArgumentValue */; } decorated.info.file = info.file; return decorated; }; } //# sourceMappingURL=decorate.js.map