decaffeinate-parser
Version:
A better AST for CoffeeScript, inspired by CoffeeScriptRedux.
221 lines (220 loc) • 9.88 kB
JavaScript
/* eslint-disable @typescript-eslint/camelcase */
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
import SourceType from 'coffee-lex/dist/SourceType';
import { Assign, Block, Call, Class, Code, Extends, For, If, In, Index, Literal, Obj, Op, Param, Slice, Switch, Try, Value, While } from 'decaffeinate-coffeescript2/lib/coffeescript/nodes';
import expandToIncludeParens from './expandToIncludeParens';
import fixInvalidLocationData from './fixInvalidLocationData';
import locationDataFromSourceRange from './locationDataFromSourceRange';
import locationWithLastPosition from './locationWithLastPosition';
import mergeLocations from './mergeLocations';
import rangeOfBracketTokensForIndexNode from './rangeOfBracketTokensForIndexNode';
import sourceRangeFromLocationData from './sourceRangeFromLocationData';
export default function fixLocations(context, node) {
var linesAndColumns = context.linesAndColumns, source = context.source;
node.eachChild(function (child) {
if (child && child.locationData) {
fixLocations(context, child);
}
return undefined;
});
node.locationData = fixInvalidLocationData(node.locationData, context.linesAndColumns);
if (node instanceof Value) {
var lastChild = node.properties[node.properties.length - 1] || node.base;
if (lastChild) {
node.locationData = locationWithLastPosition(node.locationData, lastChild.locationData);
}
}
if (node instanceof Index || node instanceof Slice) {
var rangeOfBrackets = rangeOfBracketTokensForIndexNode(context, node);
var lbracket = context.sourceTokens.tokenAtIndex(rangeOfBrackets[0]);
if (lbracket === null) {
throw new Error('Expected to find left-bracket token.');
}
var lbracketLoc = linesAndColumns.locationForIndex(lbracket.start);
if (lbracketLoc === null) {
throw new Error('Expected to find a location for the left-bracket token.');
}
var rbracketIndex = rangeOfBrackets[1].previous();
if (rbracketIndex === null) {
throw new Error('Expected to find a non-null right-bracket token index.');
}
var rbracket = context.sourceTokens.tokenAtIndex(rbracketIndex);
if (rbracket === null) {
throw new Error('Expected to find right-bracket token.');
}
var rbracketLoc = linesAndColumns.locationForIndex(rbracket.start);
if (rbracketLoc === null) {
throw new Error('Expected to find a location for the right-bracket token.');
}
node.locationData = {
first_line: lbracketLoc.line,
first_column: lbracketLoc.column,
last_line: rbracketLoc.line,
last_column: rbracketLoc.column
};
}
if (node instanceof Obj) {
var loc = node.locationData;
var start = linesAndColumns.indexForLocation({
line: loc.first_line,
column: loc.first_column
});
if (start === null) {
throw new Error('Expected to find a start index for object.');
}
var end = linesAndColumns.indexForLocation({
line: loc.last_line,
column: loc.last_column
});
if (end === null) {
throw new Error('Expected to find an end index for object.');
}
var isImplicitObject = source[start] !== '{';
if (isImplicitObject && source[end] !== ',') {
var lastChild = node.properties[node.properties.length - 1];
node.locationData = locationWithLastPosition(node.locationData, lastChild.locationData);
}
}
if (node instanceof Op) {
var lastChild = node.second;
if (lastChild) {
node.locationData = locationWithLastPosition(node.locationData, lastChild.locationData);
}
}
if (node instanceof Assign) {
var lastChild = node.value;
node.locationData = locationWithLastPosition(node.locationData, lastChild.locationData);
}
if (node instanceof In) {
var lastChild = node.array;
node.locationData = locationWithLastPosition(node.locationData, lastChild.locationData);
}
if (node instanceof Call) {
if (node.variable && !node["do"] && !node.csx) {
// `super` won't have a callee (i.e. `node.variable`)
var calleeLoc = node.variable.locationData;
var calleeEnd = linesAndColumns.indexForLocation({
line: calleeLoc.last_line,
column: calleeLoc.last_column
});
if (calleeEnd === null) {
throw new Error('Expected to find index for callee end.');
}
calleeEnd++;
// Account for soaked calls, e.g. `a?()`.
if (source[calleeEnd] === '?') {
calleeEnd += 1;
}
var isImplicitCall = source[calleeEnd] !== '(';
if (isImplicitCall) {
var lastChild = node.args[node.args.length - 1] || node.variable;
if (lastChild) {
node.locationData = locationWithLastPosition(node.locationData, lastChild.locationData);
}
}
}
}
if (node instanceof Block) {
var lastChild = node.expressions[node.expressions.length - 1];
if (lastChild) {
node.locationData = locationWithLastPosition(node.locationData, lastChild.locationData);
}
else {
// Shorten range (usually length 1, the shortest range expressible by the CoffeeScript parser) to length 0.
var sourceRange = sourceRangeFromLocationData(context, node.locationData);
node.locationData = locationDataFromSourceRange(context, {
start: sourceRange.end,
end: sourceRange.end
});
}
// Blocks can sometimes end one index before their terminating semicolon
// when really they should end exactly at that semicolon.
var blockEnd = linesAndColumns.indexForLocation({
line: node.locationData.last_line,
column: node.locationData.last_column
});
if (blockEnd === null) {
throw new Error('Expected to find index for block end.');
}
if (source[blockEnd + 1] === ';') {
blockEnd++;
var loc = linesAndColumns.locationForIndex(blockEnd);
if (loc === null) {
throw new Error('Expected to find location for block end.');
}
node.locationData.last_line = loc.line;
node.locationData.last_column = loc.column;
}
// The CS2 AST doesn't include the surrounding parens in a block, which can cause trouble with
// things like postfix loops with parenthesized bodies. Expand every block to include any
// surrounding parens.
node.locationData = expandToIncludeParens(context, node.locationData);
}
if (node instanceof If) {
var lastChild = node.elseBody || node.body;
node.locationData = mergeLocations(node.locationData, lastChild.locationData);
}
if (node instanceof For || node instanceof While) {
var lastChild = node.body;
if (lastChild) {
node.locationData = mergeLocations(node.locationData, lastChild.locationData);
}
}
if (node instanceof Param) {
if (!node.splat) {
var lastChild = node.value || node.name;
node.locationData = locationWithLastPosition(node.locationData, lastChild.locationData);
}
}
if (node instanceof Code) {
if (node.body) {
node.locationData = locationWithLastPosition(node.locationData, node.body.locationData);
}
}
if (node instanceof Class) {
var lastChild = node.body;
node.locationData = locationWithLastPosition(node.locationData, lastChild.locationData);
}
if (node instanceof Switch) {
var lastChild = node.otherwise || node.cases[node.cases.length - 1][1];
node.locationData = locationWithLastPosition(node.locationData, lastChild.locationData);
}
if (node instanceof Try) {
var lastChild = node.ensure || node.recovery || node.errorVariable || node.attempt;
if (lastChild) {
node.locationData = locationWithLastPosition(node.locationData, lastChild.locationData);
}
}
if (node instanceof Extends) {
var lastChild = node.parent;
node.locationData = locationWithLastPosition(node.locationData, lastChild.locationData);
}
if (node instanceof Literal) {
// Heregexp flags have an incorrect location, so detect that case and adjust
// the end location to be correct.
var endIndex = linesAndColumns.indexForLocation({
line: node.locationData.last_line,
column: node.locationData.last_column
});
if (endIndex !== null) {
var tokenIndex = context.sourceTokens.indexOfTokenNearSourceIndex(endIndex);
var token = context.sourceTokens.tokenAtIndex(tokenIndex);
if (token && token.type === SourceType.HEREGEXP_END) {
var location = linesAndColumns.locationForIndex(token.end - 1);
if (location) {
node.locationData = __assign(__assign({}, node.locationData), { last_line: location.line, last_column: location.column });
}
}
}
}
}