decaffeinate-parser
Version:
A better AST for CoffeeScript, inspired by CoffeeScriptRedux.
208 lines (207 loc) • 9.27 kB
JavaScript
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
import { SourceType } from 'coffee-lex';
import { Literal, Value } from 'decaffeinate-coffeescript2/lib/coffeescript/nodes';
import { inspect } from 'util';
import { Await, BinaryOp, BitAndOp, BitNotOp, BitOrOp, BitXorOp, ChainedComparisonOp, DeleteOp, DivideOp, ExistsOp, ExpOp, EQOp, FloorDivideOp, GTEOp, GTOp, InstanceofOp, LeftShiftOp, LogicalAndOp, LogicalNotOp, LogicalOrOp, LTEOp, LTOp, ModuloOp, MultiplyOp, NewOp, NEQOp, OfOp, PlusOp, PostDecrementOp, PostIncrementOp, PreDecrementOp, PreIncrementOp, RemOp, SignedRightShiftOp, SubtractOp, TypeofOp, UnaryNegateOp, UnaryPlusOp, UnsignedRightShiftOp, Yield, YieldFrom } from '../nodes';
import getLocation from '../util/getLocation';
import getOperatorInfoInRange from '../util/getOperatorInfoInRange';
import isChainedComparison from '../util/isChainedComparison';
import isImplicitPlusOp from '../util/isImplicitPlusOp';
import makeString from '../util/makeString';
import UnsupportedNodeError from '../util/UnsupportedNodeError';
import unwindChainedComparison from '../util/unwindChainedComparison';
import mapAny from './mapAny';
export default function mapOp(context, node) {
if (isChainedComparison(node)) {
return mapChainedComparisonOp(context, node);
}
else {
return mapOpWithoutChainedComparison(context, node);
}
}
function mapChainedComparisonOp(context, node) {
var _a = getLocation(context, node), line = _a.line, column = _a.column, start = _a.start, end = _a.end, raw = _a.raw;
var coffeeOperands = unwindChainedComparison(node);
var operands = coffeeOperands.map(function (operand) { return mapAny(context, operand); });
var operators = [];
for (var i = 0; i < operands.length - 1; i++) {
var left = operands[i];
var right = operands[i + 1];
operators.push(getOperatorInfoInRange(context, left.end - 1, right.start));
}
return new ChainedComparisonOp(line, column, start, end, raw, operands, operators);
}
function mapOpWithoutChainedComparison(context, node) {
switch (node.operator) {
case '===':
return mapBinaryOp(context, node, EQOp);
case '!==':
return mapBinaryOp(context, node, NEQOp);
case '!':
return mapUnaryOp(context, node, LogicalNotOp);
case '+':
return mapPlusOp(context, node);
case '-':
return mapBinaryOrUnaryOp(context, node, SubtractOp, UnaryNegateOp);
case '&&':
return mapBinaryOp(context, node, LogicalAndOp);
case '||':
return mapBinaryOp(context, node, LogicalOrOp);
case '*':
return mapBinaryOp(context, node, MultiplyOp);
case '/':
return mapBinaryOp(context, node, DivideOp);
case '//':
return mapBinaryOp(context, node, FloorDivideOp);
case '?':
return mapBinaryOp(context, node, ExistsOp);
case '<':
return mapBinaryOp(context, node, LTOp);
case '<=':
return mapBinaryOp(context, node, LTEOp);
case '>':
return mapBinaryOp(context, node, GTOp);
case '>=':
return mapBinaryOp(context, node, GTEOp);
case '++':
return mapUnaryOp(context, node, node.flip ? PostIncrementOp : PreIncrementOp);
case '--':
return mapUnaryOp(context, node, node.flip ? PostDecrementOp : PreDecrementOp);
case 'typeof':
return mapUnaryOp(context, node, TypeofOp);
case 'instanceof':
return mapNegateableBinaryOp(context, node, InstanceofOp);
case 'delete':
return mapUnaryOp(context, node, DeleteOp);
case 'in':
return mapNegateableBinaryOp(context, node, OfOp);
case 'new':
return mapNewOp(context, node);
case '**':
return mapBinaryOp(context, node, ExpOp);
case '%':
return mapBinaryOp(context, node, RemOp);
case '%%':
return mapBinaryOp(context, node, ModuloOp);
case '&':
return mapBinaryOp(context, node, BitAndOp);
case '|':
return mapBinaryOp(context, node, BitOrOp);
case '^':
return mapBinaryOp(context, node, BitXorOp);
case '<<':
return mapBinaryOp(context, node, LeftShiftOp);
case '>>':
return mapBinaryOp(context, node, SignedRightShiftOp);
case '>>>':
return mapBinaryOp(context, node, UnsignedRightShiftOp);
case '~':
return mapUnaryOp(context, node, BitNotOp);
case 'yield':
return mapYieldOp(context, node);
case 'yield*':
return mapUnaryOp(context, node, YieldFrom);
case 'await':
return mapUnaryOp(context, node, Await);
}
throw new UnsupportedNodeError(node);
}
function mapPlusOp(context, node) {
if (node.second) {
if (isImplicitPlusOp(node, context)) {
return makeString(context, node);
}
else {
return mapBinaryOp(context, node, PlusOp);
}
}
return mapUnaryOp(context, node, UnaryPlusOp);
}
function mapNewOp(context, node) {
if (node.second) {
throw new Error("unexpected 'new' operator with multiple operands: " + inspect(node));
}
var _a = getLocation(context, node), line = _a.line, column = _a.column, start = _a.start, end = _a.end, raw = _a.raw;
return new NewOp(line, column, start, end, raw, mapAny(context, node.first), []);
}
function mapYieldOp(context, node) {
var _a = getLocation(context, node), line = _a.line, column = _a.column, start = _a.start, end = _a.end, raw = _a.raw;
if (isBareYield(node)) {
return new Yield(line, column, start, end, raw, null);
}
else {
return new Yield(line, column, start, end, raw, mapAny(context, node.first));
}
}
function isBareYield(node) {
return (node.first instanceof Value &&
node.first.base instanceof Literal &&
node.first.base.value === '');
}
function mapBinaryOp(context, node, Op) {
var _a = getLocation(context, node), line = _a.line, column = _a.column, start = _a.start, end = _a.end, raw = _a.raw;
if (!node.second) {
throw new Error("unexpected '" + node.operator + "' operator with only one operand: " + inspect(node));
}
return new Op(line, column, start, end, raw, mapAny(context, node.first), mapAny(context, node.second));
}
function mapUnaryOp(context, node, Op) {
var _a = getLocation(context, node), line = _a.line, column = _a.column, start = _a.start, end = _a.end, raw = _a.raw;
if (node.second) {
throw new Error("unexpected '" + node.operator + "' operator with two operands: " + inspect(node));
}
return new Op(line, column, start, end, raw, mapAny(context, node.first));
}
function mapBinaryOrUnaryOp(context, node, BinaryOp, UnaryOp) {
if (node.second) {
return mapBinaryOp(context, node, BinaryOp);
}
else {
return mapUnaryOp(context, node, UnaryOp);
}
}
/**
* This class exists only to serve as a temporary binary operator, do not use.
*/
var TemporaryBinaryOp = /** @class */ (function (_super) {
__extends(TemporaryBinaryOp, _super);
function TemporaryBinaryOp(line, column, start, end, raw, left, right) {
return _super.call(this, 'TEMPORARY', line, column, start, end, raw, left, right) || this;
}
return TemporaryBinaryOp;
}(BinaryOp));
function tokenStartsWith(prefix, context, token) {
return (context.source.slice(token.start, token.start + prefix.length) === prefix);
}
function mapNegateableBinaryOp(context, node, Op) {
var _a = mapBinaryOp(context, node, TemporaryBinaryOp), line = _a.line, column = _a.column, start = _a.start, end = _a.end, raw = _a.raw, left = _a.left, right = _a.right;
var lastTokenIndexOfLeft = context.sourceTokens.indexOfTokenEndingAtSourceIndex(left.end);
var firstTokenIndexOfRight = context.sourceTokens.indexOfTokenStartingAtSourceIndex(right.start);
var isNot = false;
if (lastTokenIndexOfLeft) {
for (var i = lastTokenIndexOfLeft.next(); i && i !== firstTokenIndexOfRight; i = i.next()) {
var token = context.sourceTokens.tokenAtIndex(i);
if (token &&
(token.type === SourceType.OPERATOR ||
token.type === SourceType.RELATION)) {
isNot =
tokenStartsWith('not', context, token) ||
tokenStartsWith('!', context, token);
break;
}
}
}
return new Op(line, column, start, end, raw, left, right, isNot);
}