js-slang
Version:
Javascript-based implementations of Source, written in Typescript
148 lines • 5.59 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.findAncestors = exports.isInLoc = exports.findDeclarationNode = exports.findIdentifierNode = void 0;
const walkers_1 = require("./utils/walkers");
// Finds the innermost node that matches the given location
function findIdentifierNode(root, context, loc) {
function findByLocationPredicate(type, node) {
const location = node.loc;
const nodeType = node.type;
if (nodeType && location) {
return (nodeType === 'Identifier' &&
location.start.line === loc.line &&
location.start.column <= loc.column &&
location.end.column >= loc.column);
}
return false;
}
const found = (0, walkers_1.findNodeAt)(root, undefined, undefined, findByLocationPredicate, customWalker);
return found?.node;
}
exports.findIdentifierNode = findIdentifierNode;
// Recursively searches up the ancestors of the identifier from innermost to outermost scope
function findDeclarationNode(program, identifier) {
const ancestors = findAncestors(program, identifier);
if (!ancestors)
return undefined;
const declarations = [];
for (const root of ancestors) {
(0, walkers_1.recursive)(root, undefined, {
BlockStatement(node, state, callback) {
if (containsNode(node, identifier)) {
node.body.map(n => callback(n, state));
}
},
ForStatement(node, state, callback) {
if (containsNode(node, identifier)) {
callback(node.init, state);
callback(node.body, state);
}
},
FunctionDeclaration(node, state, callback) {
if (node.id && node.id.name === identifier.name) {
declarations.push(node.id);
}
else if (containsNode(node, identifier)) {
const param = node.params.find(n => n.name === identifier.name);
if (param) {
declarations.push(param);
}
else {
callback(node.body, state);
}
}
},
ArrowFunctionExpression(node, state, callback) {
if (containsNode(node, identifier)) {
const param = node.params.find(n => n.name === identifier.name);
if (param) {
declarations.push(param);
}
else {
callback(node.body, state);
}
}
},
VariableDeclarator(node, _state, _callback) {
if (node.id.name === identifier.name) {
declarations.push(node.id);
}
},
ImportSpecifier(node, _state, _callback) {
if (node.imported.name === identifier.name) {
declarations.push(node.imported);
}
}
});
if (declarations.length > 0) {
return declarations.shift();
}
}
return undefined;
}
exports.findDeclarationNode = findDeclarationNode;
function containsNode(nodeOuter, nodeInner) {
const outerLoc = nodeOuter.loc;
const innerLoc = nodeInner.loc;
return (outerLoc != null &&
innerLoc != null &&
isInLoc(innerLoc.start.line, innerLoc.start.column, outerLoc) &&
isInLoc(innerLoc.end.line, innerLoc.end.column, outerLoc));
}
// This checks if a given (line, col) value is part of another node.
function isInLoc(line, col, location) {
if (location == null) {
return false;
}
if (location.start.line < line && location.end.line > line) {
return true;
}
else if (location.start.line === line && location.end.line > line) {
return location.start.column <= col;
}
else if (location.start.line < line && location.end.line === line) {
return location.end.column >= col;
}
else if (location.start.line === line && location.end.line === line) {
if (location.start.column <= col && location.end.column >= col) {
return true;
}
else {
return false;
}
}
else {
return false;
}
}
exports.isInLoc = isInLoc;
function findAncestors(root, identifier) {
let foundAncestors = [];
(0, walkers_1.ancestor)(root, {
Identifier: (node, ancestors) => {
if (identifier.name === node.name && identifier.loc === node.loc) {
foundAncestors = Object.assign([], ancestors).reverse();
foundAncestors.shift(); // Remove the identifier node
}
},
/* We need a separate visitor for VariablePattern because
acorn walk ignores Identifers on the left side of expressions.
Here is a github issue in acorn-walk related to this:
https://github.com/acornjs/acorn/issues/686
*/
VariablePattern: (node, ancestors) => {
if (identifier.name === node.name && identifier.loc === node.loc) {
foundAncestors = Object.assign([], ancestors).reverse();
}
}
}, customWalker);
return foundAncestors;
}
exports.findAncestors = findAncestors;
const customWalker = {
...walkers_1.base,
ImportSpecifier(node, st, c) {
c(node.imported, st, 'Expression');
}
};
//# sourceMappingURL=finder.js.map
;