UNPKG

cst

Version:

JavaScript CST Implementation

588 lines (502 loc) 17.7 kB
/** * Element assertion class. * Used in specific Node types to check children for syntax correctness. * */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } var ElementAssert = (function () { /** * @param {Element[]} elements */ function ElementAssert(elements) { _classCallCheck(this, ElementAssert); this._elements = elements; if (elements.length > 0) { this._navigate(0); } } _createClass(ElementAssert, [{ key: 'assertToken', /** * Asserts that the current element is a token. * Can also check for token type and value. * * @param {String} [tokenType] * @param {String|Object} [tokenValue] if object is given, checks if value of token exists as object key. */ value: function assertToken(tokenType, tokenValue) { var _ref = this._currentElement || {}; var isToken = _ref.isToken; var type = _ref.type; var value = _ref.value; if (!isToken) { throw new Error('Token expected but "' + type + '" found'); } if (arguments.length > 0 && type !== tokenType) { throw new Error('Expected token type "' + tokenType + '" but "' + type + '" found'); } if (arguments.length === 2) { if (typeof tokenValue === 'object' && tokenValue !== null) { if (!tokenValue[value]) { throw new Error('Expected token value (' + Object.keys(tokenValue).join(', ') + ') but ' + value + ' found'); } } else if (tokenValue !== value) { throw new Error('Expected token value "' + tokenValue + '" but ' + value + ' found'); } } } /** * Asserts that the current element is a node. * Can also check for node type. * * @param {String} nodeType */ }, { key: 'assertNode', value: function assertNode(nodeType) { var _ref2 = this._currentElement || {}; var isNode = _ref2.isNode; var type = _ref2.type; if (!isNode) { throw new Error('Node expected but "' + type + '" found'); } if (arguments.length > 0 && type !== nodeType) { throw new Error('Expected node type "' + nodeType + '" but "' + type + '" found'); } } /** * Asserts that the current element is a node. * Can also check if any of the node type are satisfied. * * @param {Array} nodeTypes */ }, { key: 'assertOneOfNode', value: function assertOneOfNode(nodeTypes) { var _ref3 = this._currentElement || {}; var isNode = _ref3.isNode; var type = _ref3.type; if (!isNode) { throw new Error('Node expected but "' + type + '" found'); } if (arguments.length > 0 && nodeTypes.indexOf(type) === -1) { throw new Error('Expected one of node types "' + nodeTypes + '" but "' + type + '" found'); } } /** * Asserts that the current element is an expression. */ }, { key: 'assertExpression', value: function assertExpression() { var _ref4 = this._currentElement || {}; var isExpression = _ref4.isExpression; var type = _ref4.type; if (!isExpression) { throw new Error('Expression expected but "' + type + '" found'); } } /** * Asserts that the current element is an assignment. */ }, { key: 'assertAssignable', value: function assertAssignable() { var _ref5 = this._currentElement || {}; var isAssignable = _ref5.isAssignable; var type = _ref5.type; if (!isAssignable) { throw new Error('Expected assignable expression but ' + type + ' found.'); } } /** * Asserts that the current element is a pattern. */ }, { key: 'assertPattern', value: function assertPattern() { var _ref6 = this._currentElement || {}; var isPattern = _ref6.isPattern; var type = _ref6.type; if (!isPattern) { throw new Error('Expected pattern but ' + type + ' found.'); } } /** * Asserts that the current element is a statement. */ }, { key: 'assertStatement', value: function assertStatement() { var _ref7 = this._currentElement || {}; var isStatement = _ref7.isStatement; var type = _ref7.type; if (!isStatement) { throw new Error('Statement expected but "' + type + '" found'); } } /** * Asserts that the current element is a statement. */ }, { key: 'assertModuleSpecifier', value: function assertModuleSpecifier() { var _ref8 = this._currentElement || {}; var isModuleSpecifier = _ref8.isModuleSpecifier; var type = _ref8.type; if (!isModuleSpecifier) { throw new Error('ModuleSpecifier expected but "' + type + '" found'); } } /** * Asserts that the end of child list was reached. */ }, { key: 'assertEnd', value: function assertEnd() { if (this._currentElement !== undefined) { var _ref9 = this._currentElement || {}; var type = _ref9.type; throw new Error('Expected end of node list but "' + type + '" found'); } } /** * Checks if the current element is a token. * Can also check for token type and value. * * @param {String} [tokenType] * @param {String|Object} [tokenValue] if object is given, checks if value of token exists as object key. * @returns {Boolean} */ }, { key: 'isToken', value: function isToken(tokenType, tokenValue) { var _ref10 = this._currentElement || {}; var isToken = _ref10.isToken; var type = _ref10.type; var value = _ref10.value; if (!isToken || arguments.length > 0 && type !== tokenType) { return false; } if (arguments.length === 2) { if (typeof tokenValue === 'object' && tokenValue !== null) { return Boolean(tokenValue[value]); } return tokenValue === value; } return true; } /** * Checks if the current element is a node. * Can also check for token type and value. * * @param {String} [nodeType] * @returns {Boolean} */ }, { key: 'isNode', value: function isNode(nodeType) { var _ref11 = this._currentElement || {}; var isNode = _ref11.isNode; var type = _ref11.type; return !(!isNode || arguments.length > 0 && type !== nodeType); } /** * Checks if the current element is a statement. * * @returns {Boolean} */ }, { key: 'isStatement', value: function isStatement() { var _ref12 = this._currentElement || {}; var isStatement = _ref12.isStatement; return isStatement; } /** * Checks if current element is token (can also check type and value), * returns current element and move pointer to the next element. * * @param {String} [tokenType] * @param {String|Object} [tokenValue] * @returns {Element|null} */ }, { key: 'passToken', value: function passToken(tokenType, tokenValue) { this.assertToken.apply(this, arguments); var token = this._currentElement; this.moveNext(); return token; } /** * Checks if current element is a node (can also check type), * returns current element and move pointer to the next element. * * @param {String} [nodeType] * @returns {Element|null} */ }, { key: 'passNode', value: function passNode(nodeType) { this.assertNode.apply(this, arguments); var node = this._currentElement; this.moveNext(); return node; } /** * Checks if current element is a node (can also check if any types are satisfied), * returns current element and move pointer to the next element. * * @param {Array} [nodeTypes] * @returns {Element|null} */ }, { key: 'passOneOfNode', value: function passOneOfNode(nodeTypes) { this.assertOneOfNode(nodeTypes); var node = this._currentElement; this.moveNext(); return node; } /** * Checks if current element is an expression, * returns current element and move pointer to the next element. * Ignores parentheses. * * @returns {Element} */ }, { key: 'passExpression', value: function passExpression() { return this._passExpressionInParens(function (expression) { return expression.isExpression; }); } /** * Checks if current element is an expression or whitespace * returns current element and move pointer to the next element. * Ignores parentheses. * * @returns {Element} */ }, { key: 'passExpressionOrWhitespace', value: function passExpressionOrWhitespace() { return this._passExpressionInParens(function (expression) { return expression.isExpression || expression.isWhitespace; }); } /** * Checks if current element is an expression or super, * returns current element and move pointer to the next element. * Ignores parentheses. * * @returns {Element} */ }, { key: 'passExpressionOrSuper', value: function passExpressionOrSuper() { return this._passExpressionInParens(function (expression) { return expression.isExpression || expression.type === 'Super'; }); } /** * Checks if current element is an expression or SpreadElement, * returns current element and move pointer to the next element. * Ignores parentheses. * * @returns {Element} */ }, { key: 'passExpressionOrSpreadElement', value: function passExpressionOrSpreadElement() { return this._passExpressionInParens(function (expression) { return expression.isExpression || expression.type === 'SpreadElement'; }); } /** * Passes expression ignoring parentheses, returns element and move pointer to the next element. * * @param {Function} assertCallback * @returns {Element} * @private */ }, { key: '_passExpressionInParens', value: function _passExpressionInParens(assertCallback) { var openParens = 0; while (this._currentElement.type === 'Punctuator' && this._currentElement.value === '(') { openParens++; this.moveNext(); this.skipNonCode(); } var expression = this._currentElement; if (!expression) { throw new Error('Could not match an expression'); } if (!assertCallback(expression)) { throw new Error('Expression expected but "' + expression.type + '" found'); } this.moveNext(); while (openParens--) { this.skipNonCode(); this.assertToken('Punctuator', ')'); this.moveNext(); } return expression; } /** * Checks if current element is an assignable, returns current element and move pointer to the next element. * Ignores parentheses. * * @returns {Element} */ }, { key: 'passAssignable', value: function passAssignable() { return this._passExpressionInParens(function (expression) { return expression.isAssignable; }); } /** * Checks if current element is a statement, * returns current element and move pointer to the next element. * * @returns {Element} */ }, { key: 'passStatement', value: function passStatement() { this.assertStatement(); var result = this._currentElement; this.moveNext(); if (!result) { throw new Error('Could not match statement'); } return result; } /** * Checks if current element is a pattern, * returns current element and move pointer to the next element. * * @returns {Element|null} */ }, { key: 'passPattern', value: function passPattern() { this.assertPattern(); var result = this._currentElement; this.moveNext(); if (!result) { throw new Error('Could not match pattern'); } return result; } /** * Checks if current element is a module specifier, * returns current element and move pointer to the next element. * * @returns {Element|null} */ }, { key: 'passModuleSpecifier', value: function passModuleSpecifier() { this.assertModuleSpecifier(); var result = this._currentElement; this.moveNext(); return result; } /** * Skips comments and whitespace. */ }, { key: 'skipNonCode', value: function skipNonCode() { while (true) { var _ref13 = this._currentElement || {}; var isCode = _ref13.isCode; if (isCode !== false) { break; } this.moveNext(); } } /** * Skips comments and whitespace on the same line. */ }, { key: 'skipSameLineNonCode', value: function skipSameLineNonCode() { while (true) { var _ref14 = this._currentElement || {}; var isCode = _ref14.isCode; if (isCode !== false) { break; } if (this._currentElement && this._currentElement.newlineCount > 0) { break; } this.moveNext(); } } /** * Skips a semicolon. */ }, { key: 'skipSemicolon', value: function skipSemicolon() { if (this._currentElement && this._currentElement.type === 'Punctuator' && this._currentElement.value === ';') { this.moveNext(); } } /** * Moves pointer (currentElement) to next element. */ }, { key: 'moveNext', value: function moveNext() { this._navigate(this._position + 1); } /** * Navigates to specified child position. * * @param {Number} position * @private */ }, { key: '_navigate', value: function _navigate(position) { this._position = position; this._currentElement = this._elements[position]; } }, { key: 'isEnd', /** * True if end of child list was reached. * * @returns {Boolean} */ get: function get() { return this._currentElement === undefined; } /** * Currect element or null if end of child list was reached. * * @returns {Element|null} */ }, { key: 'currentElement', get: function get() { return this._currentElement; } }]); return ElementAssert; })(); exports['default'] = ElementAssert; module.exports = exports['default'];