cst
Version:
JavaScript CST Implementation
592 lines (490 loc) • 17.3 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _keys = require('babel-runtime/core-js/object/keys');
var _keys2 = _interopRequireDefault(_keys);
var _typeof2 = require('babel-runtime/helpers/typeof');
var _typeof3 = _interopRequireDefault(_typeof2);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Element assertion class.
* Used in specific Node types to check children for syntax correctness.
*
*/
var ElementAssert = function () {
/**
* @param {Element[]} elements
*/
function ElementAssert(elements) {
(0, _classCallCheck3.default)(this, ElementAssert);
this._elements = elements;
if (elements.length > 0) {
this._navigate(0);
}
}
(0, _createClass3.default)(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 || {},
isToken = _ref.isToken,
type = _ref.type,
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 === 'undefined' ? 'undefined' : (0, _typeof3.default)(tokenValue)) === 'object' && tokenValue !== null) {
if (!tokenValue[value]) {
throw new Error('Expected token value (' + (0, _keys2.default)(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 || {},
isNode = _ref2.isNode,
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 || {},
isNode = _ref3.isNode,
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 || {},
isExpression = _ref4.isExpression,
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 || {},
isAssignable = _ref5.isAssignable,
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 || {},
isPattern = _ref6.isPattern,
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 || {},
isStatement = _ref7.isStatement,
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 || {},
isModuleSpecifier = _ref8.isModuleSpecifier,
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 || {},
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 || {},
isToken = _ref10.isToken,
type = _ref10.type,
value = _ref10.value;
if (!isToken || arguments.length > 0 && type !== tokenType) {
return false;
}
if (arguments.length === 2) {
if ((typeof tokenValue === 'undefined' ? 'undefined' : (0, _typeof3.default)(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 || {},
isNode = _ref11.isNode,
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 || {},
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 || {},
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 || {},
isCode = _ref14.isCode;
if (isCode !== false) {
break;
}
if (this.currentElement && this.currentElement.getNewlineCount() > 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];
this.isEnd = this.currentElement === undefined;
}
}]);
return ElementAssert;
}();
exports.default = ElementAssert;
//# sourceMappingURL=ElementAssert.js.map