decaffeinate-parser
Version:
A better AST for CoffeeScript, inspired by CoffeeScriptRedux.
211 lines (210 loc) • 9.52 kB
JavaScript
"use strict";
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 __());
};
})();
exports.__esModule = true;
var coffee_lex_1 = require("coffee-lex");
var nodes_1 = require("decaffeinate-coffeescript2/lib/coffeescript/nodes");
var util_1 = require("util");
var nodes_2 = require("../nodes");
var getLocation_1 = require("../util/getLocation");
var getOperatorInfoInRange_1 = require("../util/getOperatorInfoInRange");
var isChainedComparison_1 = require("../util/isChainedComparison");
var isImplicitPlusOp_1 = require("../util/isImplicitPlusOp");
var makeString_1 = require("../util/makeString");
var UnsupportedNodeError_1 = require("../util/UnsupportedNodeError");
var unwindChainedComparison_1 = require("../util/unwindChainedComparison");
var mapAny_1 = require("./mapAny");
function mapOp(context, node) {
if (isChainedComparison_1["default"](node)) {
return mapChainedComparisonOp(context, node);
}
else {
return mapOpWithoutChainedComparison(context, node);
}
}
exports["default"] = mapOp;
function mapChainedComparisonOp(context, node) {
var _a = getLocation_1["default"](context, node), line = _a.line, column = _a.column, start = _a.start, end = _a.end, raw = _a.raw;
var coffeeOperands = unwindChainedComparison_1["default"](node);
var operands = coffeeOperands.map(function (operand) { return mapAny_1["default"](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_1["default"](context, left.end - 1, right.start));
}
return new nodes_2.ChainedComparisonOp(line, column, start, end, raw, operands, operators);
}
function mapOpWithoutChainedComparison(context, node) {
switch (node.operator) {
case '===':
return mapBinaryOp(context, node, nodes_2.EQOp);
case '!==':
return mapBinaryOp(context, node, nodes_2.NEQOp);
case '!':
return mapUnaryOp(context, node, nodes_2.LogicalNotOp);
case '+':
return mapPlusOp(context, node);
case '-':
return mapBinaryOrUnaryOp(context, node, nodes_2.SubtractOp, nodes_2.UnaryNegateOp);
case '&&':
return mapBinaryOp(context, node, nodes_2.LogicalAndOp);
case '||':
return mapBinaryOp(context, node, nodes_2.LogicalOrOp);
case '*':
return mapBinaryOp(context, node, nodes_2.MultiplyOp);
case '/':
return mapBinaryOp(context, node, nodes_2.DivideOp);
case '//':
return mapBinaryOp(context, node, nodes_2.FloorDivideOp);
case '?':
return mapBinaryOp(context, node, nodes_2.ExistsOp);
case '<':
return mapBinaryOp(context, node, nodes_2.LTOp);
case '<=':
return mapBinaryOp(context, node, nodes_2.LTEOp);
case '>':
return mapBinaryOp(context, node, nodes_2.GTOp);
case '>=':
return mapBinaryOp(context, node, nodes_2.GTEOp);
case '++':
return mapUnaryOp(context, node, node.flip ? nodes_2.PostIncrementOp : nodes_2.PreIncrementOp);
case '--':
return mapUnaryOp(context, node, node.flip ? nodes_2.PostDecrementOp : nodes_2.PreDecrementOp);
case 'typeof':
return mapUnaryOp(context, node, nodes_2.TypeofOp);
case 'instanceof':
return mapNegateableBinaryOp(context, node, nodes_2.InstanceofOp);
case 'delete':
return mapUnaryOp(context, node, nodes_2.DeleteOp);
case 'in':
return mapNegateableBinaryOp(context, node, nodes_2.OfOp);
case 'new':
return mapNewOp(context, node);
case '**':
return mapBinaryOp(context, node, nodes_2.ExpOp);
case '%':
return mapBinaryOp(context, node, nodes_2.RemOp);
case '%%':
return mapBinaryOp(context, node, nodes_2.ModuloOp);
case '&':
return mapBinaryOp(context, node, nodes_2.BitAndOp);
case '|':
return mapBinaryOp(context, node, nodes_2.BitOrOp);
case '^':
return mapBinaryOp(context, node, nodes_2.BitXorOp);
case '<<':
return mapBinaryOp(context, node, nodes_2.LeftShiftOp);
case '>>':
return mapBinaryOp(context, node, nodes_2.SignedRightShiftOp);
case '>>>':
return mapBinaryOp(context, node, nodes_2.UnsignedRightShiftOp);
case '~':
return mapUnaryOp(context, node, nodes_2.BitNotOp);
case 'yield':
return mapYieldOp(context, node);
case 'yield*':
return mapUnaryOp(context, node, nodes_2.YieldFrom);
case 'await':
return mapUnaryOp(context, node, nodes_2.Await);
}
throw new UnsupportedNodeError_1["default"](node);
}
function mapPlusOp(context, node) {
if (node.second) {
if (isImplicitPlusOp_1["default"](node, context)) {
return makeString_1["default"](context, node);
}
else {
return mapBinaryOp(context, node, nodes_2.PlusOp);
}
}
return mapUnaryOp(context, node, nodes_2.UnaryPlusOp);
}
function mapNewOp(context, node) {
if (node.second) {
throw new Error("unexpected 'new' operator with multiple operands: " + util_1.inspect(node));
}
var _a = getLocation_1["default"](context, node), line = _a.line, column = _a.column, start = _a.start, end = _a.end, raw = _a.raw;
return new nodes_2.NewOp(line, column, start, end, raw, mapAny_1["default"](context, node.first), []);
}
function mapYieldOp(context, node) {
var _a = getLocation_1["default"](context, node), line = _a.line, column = _a.column, start = _a.start, end = _a.end, raw = _a.raw;
if (isBareYield(node)) {
return new nodes_2.Yield(line, column, start, end, raw, null);
}
else {
return new nodes_2.Yield(line, column, start, end, raw, mapAny_1["default"](context, node.first));
}
}
function isBareYield(node) {
return (node.first instanceof nodes_1.Value &&
node.first.base instanceof nodes_1.Literal &&
node.first.base.value === '');
}
function mapBinaryOp(context, node, Op) {
var _a = getLocation_1["default"](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: " + util_1.inspect(node));
}
return new Op(line, column, start, end, raw, mapAny_1["default"](context, node.first), mapAny_1["default"](context, node.second));
}
function mapUnaryOp(context, node, Op) {
var _a = getLocation_1["default"](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: " + util_1.inspect(node));
}
return new Op(line, column, start, end, raw, mapAny_1["default"](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;
}(nodes_2.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 === coffee_lex_1.SourceType.OPERATOR ||
token.type === coffee_lex_1.SourceType.RELATION)) {
isNot =
tokenStartsWith('not', context, token) ||
tokenStartsWith('!', context, token);
break;
}
}
}
return new Op(line, column, start, end, raw, left, right, isNot);
}