UNPKG

decaffeinate-parser

Version:

A better AST for CoffeeScript, inspired by CoffeeScriptRedux.

221 lines (220 loc) 9.88 kB
/* 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 }); } } } } }