@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
274 lines • 12.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.RNode = exports.ROther = exports.RFunctions = exports.RConstructs = exports.RLoopConstructs = exports.RSingleNode = exports.RConstant = void 0;
const range_1 = require("../../../../util/range");
const type_1 = require("./type");
const r_access_1 = require("./nodes/r-access");
const visitor_1 = require("./processing/visitor");
const assert_1 = require("../../../../util/assert");
const documentation_provider_1 = require("../../../roxygen2/documentation-provider");
/**
* Helper object to provide helper functions for {@link RConstant|RConstants}.
* @see {@link RNode} - for more general helper functions for all nodes
*/
exports.RConstant = {
name: 'RConstant',
/**
* Type guard for {@link RConstant} nodes, i.e. checks whether a node is a number, string or logical constant.
* If you need the specific type, please either use the respective type guards (e.g., {@link RNumber.is}) or check the `type` node directly.
*/
is(node) {
if (!node) {
return false;
}
const t = node.type;
return t === type_1.RType.Number || t === type_1.RType.String || t === type_1.RType.Logical;
},
/**
* A set of all types of constants in the normalized AST, i.e. number, string and logical constants.
*/
constantTypes: new Set([type_1.RType.Number, type_1.RType.String, type_1.RType.Logical])
};
/**
* Represents a leaf node in the normalized AST, i.e. a node that does not have any children. This includes comment, symbol, constant, break, next and line directive nodes.
*/
exports.RSingleNode = {
name: 'RSingleNode',
/**
* Type guard for {@link RSingleNode} nodes, i.e. checks whether a node is a comment, symbol, constant, break, next or line directive.
* If you need the specific type, please either use the respective type guards (e.g., {@link RComment.is}) or check the `type` node directly.
*/
is(node) {
if (!node) {
return false;
}
const t = node.type;
return t === type_1.RType.Comment || t === type_1.RType.Symbol || exports.RConstant.constantTypes.has(t) || t === type_1.RType.Break || t === type_1.RType.Next || t === type_1.RType.LineDirective;
},
/**
* A set of all types of single nodes in the normalized AST, i.e. comment, symbol, constant, break, next and line directive nodes.
*/
singleNodeTypes: new Set([type_1.RType.Comment, type_1.RType.Symbol, type_1.RType.Break, type_1.RType.Next, type_1.RType.LineDirective, ...exports.RConstant.constantTypes])
};
exports.RLoopConstructs = {
name: 'RLoopConstructs',
/**
* Type guard for {@link RLoopConstructs} nodes, i.e. checks whether a node is a for, repeat or while loop.
* If you need the specific type, please either use the respective type guards (e.g., {@link RForLoop.is}) or check the `type` node directly.
*/
is(node) {
if (!node) {
return false;
}
const t = node.type;
return t === type_1.RType.ForLoop || t === type_1.RType.RepeatLoop || t === type_1.RType.WhileLoop;
},
/**
* A set of all types of loop constructs in the normalized AST, i.e. for, repeat and while loops.
*/
loopConstructTypes: new Set([type_1.RType.ForLoop, type_1.RType.RepeatLoop, type_1.RType.WhileLoop])
};
exports.RConstructs = {
name: 'RConstructs',
/**
* Type guard for {@link RConstructs} nodes, i.e. checks whether a node is a for, repeat or while loop or an if-then-else construct.
* If you need the specific type, please either use the respective type guards (e.g., {@link RForLoop.is}) or check the `type` node directly.
*/
is(node) {
if (!node) {
return false;
}
const t = node.type;
return exports.RLoopConstructs.loopConstructTypes.has(t) || t === type_1.RType.IfThenElse;
},
/**
* A set of all types of constructs in the normalized AST, i.e. for, repeat and while loops and if-then-else constructs.
*/
constructTypes: new Set([...exports.RLoopConstructs.loopConstructTypes, type_1.RType.IfThenElse])
};
exports.RFunctions = {
name: 'RFunctions',
/**
* Type guard for {@link RFunctions} nodes, i.e. checks whether a node is a function definition, function call, parameter or argument.
* If you need the specific type, please either use the respective type guards (e.g., {@link RFunctionDefinition.is}) or check the `type` node directly.
*/
is(node) {
if (!node) {
return false;
}
const t = node.type;
return t === type_1.RType.FunctionDefinition || t === type_1.RType.FunctionCall || t === type_1.RType.Parameter || t === type_1.RType.Argument;
},
/**
* A set of all types of function-related nodes in the normalized AST, i.e. function definitions, function calls, parameters and arguments.
*/
functionTypes: new Set([type_1.RType.FunctionDefinition, type_1.RType.FunctionCall, type_1.RType.Parameter, type_1.RType.Argument])
};
exports.ROther = {
name: 'ROther',
/**
* Type guard for {@link ROther} nodes, i.e. checks whether a node is a comment or line directive.
* If you need the specific type, please either use the respective type guards (e.g., {@link RComment.is}) or check the `type` node directly.
*/
is(node) {
if (!node) {
return false;
}
const t = node.type;
return t === type_1.RType.Comment || t === type_1.RType.LineDirective;
}
};
/**
* Helper object to provide helper functions for {@link RNode|RNodes}.
* For the individual type checks, please consult the individual vertices, e.g. {@link RPipe.is}.
* Some vertices also have a {@link RPipe.availableFromRVersion} property that indicates from which R version they are available,
* so you can check for that as well if needed.
* @see {@link DefaultNormalizedAstFold} - for a more powerful way to traverse the normalized AST
*/
exports.RNode = {
name: 'RNode',
/**
* A helper function to retrieve the location of a given node, if available.
* @see SourceLocation.fromNode
*/
getLocation(node) {
return range_1.SourceLocation.fromNode(node);
},
/**
* A helper function to retrieve the id of a given node, if available.
*/
getId(node) {
return node.info.id;
},
/**
* A helper function to retrieve the type of a given node.
*/
getType(node) {
return node.type;
},
/**
* Visits all node ids within a tree given by a respective root node using a depth-first search with prefix order.
* @param nodes - The root id nodes to start collecting from
* @param onVisit - Called before visiting the subtree of each node. Can be used to stop visiting the subtree starting with this node (return `true` stop)
* @param onExit - Called after the subtree of a node has been visited, called for leafs too (even though their subtree is empty)
* @see {@link RProject.visitAst} - to visit all nodes in a project
*/
visitAst(nodes, onVisit, onExit) {
return new visitor_1.NodeVisitor(onVisit, onExit).visit(nodes);
},
/**
* Collects all node ids within a tree given by a respective root node
* @param nodes - The root id nodes to start collecting from
* @see {@link collectAllIdsWithStop} - to stop collecting at certain nodes
* @see {@link RProject.collectAllIds} - to collect all ids within a project
*/
collectAllIds(nodes) {
const ids = new Set();
exports.RNode.visitAst(nodes, node => {
ids.add(node.info.id);
});
return ids;
},
/**
* Collects all direct children of a given node, i.e. all nodes that are directly reachable via a property of the given node.
*/
directChildren(node) {
const type = node.type;
switch (type) {
case type_1.RType.FunctionCall: return [node.named ? node.functionName : node.calledFunction, ...node.arguments];
case type_1.RType.FunctionDefinition: return [...node.parameters, node.body];
case type_1.RType.ExpressionList: return node.grouping ? [node.grouping[0], ...node.children, node.grouping[1]] : node.children;
case type_1.RType.ForLoop: return [node.variable, node.vector, node.body];
case type_1.RType.WhileLoop: return [node.condition, node.body];
case type_1.RType.RepeatLoop: return [node.body];
case type_1.RType.IfThenElse: return node.otherwise ? [node.condition, node.then, node.otherwise] : [node.condition, node.then];
case type_1.RType.BinaryOp:
case type_1.RType.Pipe: return [node.lhs, node.rhs];
case type_1.RType.UnaryOp: return [node.operand];
case type_1.RType.Parameter: return node.defaultValue ? [node.name, node.defaultValue] : [node.name];
case type_1.RType.Argument: return node.name && node.value ? [node.name, node.value] : node.name ? [node.name] : node.value ? [node.value] : [];
case type_1.RType.Access: return r_access_1.RAccess.isIndex(node) ? [node.accessed, ...node.access] : [node.accessed];
case type_1.RType.Symbol:
case type_1.RType.Logical:
case type_1.RType.Number:
case type_1.RType.String:
case type_1.RType.Comment:
case type_1.RType.Break:
case type_1.RType.Next:
case type_1.RType.LineDirective: return [];
default:
(0, assert_1.assertUnreachable)(type);
}
},
/**
* Returns the direct parent of a node.
* Usually, only root nodes do not have a parent, and you can assume that there is a
* linear chain of parents leading to the root node.
* @see {@link iterateParents} - to get all parents of a node
*/
directParent(node, idMap) {
const parentId = node.info.parent;
if (parentId === undefined) {
return undefined;
}
return idMap.get(parentId);
},
/**
* Returns an iterable of all parents of a node, starting with the direct parent and ending with the root node.
*/
*iterateParents(node, idMap) {
let currentNode = node;
while (currentNode) {
currentNode = exports.RNode.directParent(currentNode, idMap);
if (currentNode) {
yield currentNode;
}
}
},
/**
* In contrast to the nesting stored in the {@link RNode} structure,
* this function calculates the depth of a node by counting the number of parents until the root node is reached.
*/
depth(node, idMap) {
let depth = 0;
let currentNode = node;
while (currentNode) {
currentNode = exports.RNode.directParent(currentNode, idMap);
depth++;
}
return depth;
},
/**
* Collects all node ids within a tree given by a respective root node, but stops collecting at nodes where the given `stop` function returns `true`.
* <p>
* This can be used to exclude certain subtrees from the collection, for example to exclude function bodies when collecting ids on the root level.
* @param nodes - The root id nodes to start collecting from
* @param stop - A function that determines whether to stop collecting at a given node, does not stop by default
* @see {@link collectAllIds} - to collect all ids without stopping
* @see {@link RProject.collectAllIdsWithStop} - to collect all ids within a project with stopping
*/
collectAllIdsWithStop(nodes, stop) {
const ids = new Set();
exports.RNode.visitAst(nodes, node => {
if (stop(node)) {
return true;
}
ids.add(node.info.id);
return false;
});
return ids;
},
/**
* A helper function to retrieve the lexeme of a given node, if available.
* If the `fullLexeme` is available, it will be returned, otherwise the `lexeme` will be returned.
*/
lexeme(node) {
return node?.info.fullLexeme ?? node?.lexeme;
},
/**
* Return the (roxygen) documentation associated with the given node, if available.
* @see {@link getDocumentationOf}
*/
documentation: documentation_provider_1.getDocumentationOf
};
//# sourceMappingURL=model.js.map