syntax-cli-prog
Version:
Syntactic analysis toolkit, language agnostic parsers generator.
394 lines (316 loc) • 11.3 kB
JavaScript
'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; }; }(); /** * The MIT License (MIT)
* Copyright (c) 2015-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
*/
var _codeUnit = require('../code-unit');
var _codeUnit2 = _interopRequireDefault(_codeUnit);
var _grammarSymbol = require('./grammar-symbol');
var _grammarSymbol2 = _interopRequireDefault(_grammarSymbol);
var _specialSymbols = require('../special-symbols');
var _colors = require('colors');
var _colors2 = _interopRequireDefault(_colors);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* A production in BNF grammar.
*/
var Production = function () {
/**
* Receives a raw production in a view of:
*
* LHS -> RHS or a short alternative
* | RHS if the LHS is the same.
*/
function Production(LHS, RHS, number, semanticAction, isShort, grammar, precedence) {
_classCallCheck(this, Production);
this._rawLHS = LHS;
this._rawRHS = RHS;
this._number = number;
this._isAugmented = number === 0;
this._isShort = !!isShort;
this._grammar = grammar;
this._normalize();
if (semanticAction == null) {
semanticAction = this._createDefaultSemanticAction();
}
this._orginialSemanticAction = semanticAction;
this._rawSemanticAction = this._rewriteNamedArg(semanticAction);
this._semanticAction = this._buildSemanticAction(this._rawSemanticAction);
this._precedence = precedence || this._calculatePrecedence();
}
/**
* Creates default semantic action for simple productions.
*/
_createClass(Production, [{
key: '_createDefaultSemanticAction',
value: function _createDefaultSemanticAction() {
if (this.getRHS().length !== 1 || this.isEpsilon()) {
return null;
}
return '$$ = $1';
}
/**
* Rewrites named arguments to positioned ones.
* $foo -> $1, ...
*/
}, {
key: '_rewriteNamedArg',
value: function _rewriteNamedArg(semanticAction) {
if (!semanticAction) {
return null;
}
var RHS = this.getRHS();
var idRe = /[a-zA-Z][a-zA-Z0-9]*/;
for (var i = 0; i < RHS.length; i++) {
var symbol = RHS[i].getSymbol();
if (!idRe.test(symbol)) {
continue;
}
var index = i + 1;
var symbolRe = new RegExp('(\\$|@)' + symbol + '\\b', 'g');
semanticAction = semanticAction.replace(symbolRe, '$1' + index);
}
return semanticAction;
}
/**
* Returns number of the production
* in the grammar.
*/
}, {
key: 'getNumber',
value: function getNumber() {
return this._number;
}
/**
* Whether this production is augmented.
*/
}, {
key: 'isAugmented',
value: function isAugmented() {
return this._isAugmented;
}
/**
* Returns LHS symbol.
*/
}, {
key: 'getLHS',
value: function getLHS() {
return this._LHS;
}
/**
* Returns an array of symbols on RHS (aka "handle").
*/
}, {
key: 'getRHS',
value: function getRHS() {
return this._RHS;
}
/**
* Same as `getRHS`, but returns raw symbols.
*/
}, {
key: 'getRHSSymbols',
value: function getRHSSymbols() {
if (!this._rhsSymbols) {
this._rhsSymbols = this._RHS.map(function (symbol) {
return symbol.getSymbol();
});
}
return this._rhsSymbols;
}
/**
* A map for faster searches whether a symbol is used on RHS.
*/
}, {
key: 'getRHSSymbolsMap',
value: function getRHSSymbolsMap() {
var _this = this;
if (!this._rhsSymbolsMap) {
this._rhsSymbolsMap = {};
this._RHS.forEach(function (symbol) {
return _this._rhsSymbolsMap[symbol.getSymbol()] = true;
});
}
return this._rhsSymbolsMap;
}
/**
* Returns precedence of this production.
*/
}, {
key: 'getPrecedence',
value: function getPrecedence() {
return this._precedence;
}
/**
* Returns original semantic action.
*/
}, {
key: 'getOriginalSemanticAction',
value: function getOriginalSemanticAction() {
return this._orginialSemanticAction;
}
/**
* Returns semantic action string.
*/
}, {
key: 'getRawSemanticAction',
value: function getRawSemanticAction() {
return this._rawSemanticAction;
}
/**
* Returns semantic action function.
*/
}, {
key: 'getSemanticAction',
value: function getSemanticAction() {
return this._semanticAction;
}
/**
* Whether this production has semantic action.
*/
}, {
key: 'hasSemanticAction',
value: function hasSemanticAction() {
return this._semanticAction !== null;
}
/**
* Executes semantic action.
*/
}, {
key: 'runSemanticAction',
value: function runSemanticAction(args) {
if (!this._semanticAction) {
return;
}
return this._semanticAction.apply(this, _toConsumableArray(args));
}
/**
* Whether this production is epsilon.
*/
}, {
key: 'isEpsilon',
value: function isEpsilon() {
var RHS = this.getRHS();
return RHS.length === 1 && RHS[0].isEpsilon();
}
/**
* String representation.
*/
}, {
key: 'toString',
value: function toString() {
return this._toKey(this._isShort);
}
/**
* String representation in full notation.
*/
}, {
key: 'toFullString',
value: function toFullString() {
return this._toKey(false);
}
}, {
key: '_toKey',
value: function _toKey() {
var isShort = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
var LHS = this._LHS.getSymbol();
var RHS = this._RHS.map(function (symbol) {
return symbol.getSymbol();
}).join(' ');
var pad = Array(LHS.length + '->'.length).join(' ');
return isShort ? pad + ' | ' + RHS : LHS + ' -> ' + RHS;
}
/**
* Constructs semantic action based on the RHS length,
* each stack entry which corresponds to a symbol is available
* as $1, $2, $3, etc. arguments. The result is $$.
*/
}, {
key: '_buildSemanticAction',
value: function _buildSemanticAction(semanticAction) {
var _this2 = this;
if (!semanticAction) {
return null;
}
// Generate the function handler only for JS language.
try {
var handler = _codeUnit2.default.createProductionHandler({
production: this,
captureLocations: this._grammar.shouldCaptureLocations()
});
return function () {
// Executing a handler mutates $$ variable, return it.
try {
handler.apply(undefined, arguments);
} catch (e) {
console.error(_colors2.default.red('\nError in handler for production ') + _colors2.default.bold(_this2.toFullString()) + ':\n\n' + _this2.getOriginalSemanticAction() + '\n');
throw e;
}
return _codeUnit2.default.getSandbox().__;
};
} catch (e) {
/* And skip for other languages, which use raw handler in generator */
}
}
}, {
key: '_normalize',
value: function _normalize() {
var LHS = _grammarSymbol2.default.get(this._rawLHS);
var RHS = [];
// If no RHS provided, assume it's ε. We support
// both formats, explicit: F -> ε, and implicit: F ->
if (!this._rawRHS) {
RHS.push(_grammarSymbol2.default.get(_specialSymbols.EPSILON));
} else {
var rhsProd = this._rawRHS.split(/\s+/);
for (var i = 0; i < rhsProd.length; i++) {
if (rhsProd[i] === '"' && rhsProd[i + 1] === '"') {
RHS.push(_grammarSymbol2.default.get('" "'));
i++;
} else {
RHS.push(_grammarSymbol2.default.get(rhsProd[i]));
}
}
}
this._LHS = LHS;
this._RHS = RHS;
}
}, {
key: '_calculatePrecedence',
value: function _calculatePrecedence() {
var operators = this._grammar.getOperators();
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = this.getRHS()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var grammarSymbol = _step.value;
var symbol = grammarSymbol.getSymbol();
if (symbol in operators) {
return operators[symbol].precedence;
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return 0;
}
}]);
return Production;
}();
exports.default = Production;