UNPKG

decaffeinate-parser

Version:

A better AST for CoffeeScript, inspired by CoffeeScriptRedux.

211 lines (210 loc) 9.52 kB
"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); }