UNPKG

decaffeinate-parser

Version:

A better AST for CoffeeScript, inspired by CoffeeScriptRedux.

208 lines (207 loc) 9.27 kB
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); }