UNPKG

syntax-cli-prog

Version:

Syntactic analysis toolkit, language agnostic parsers generator.

530 lines (429 loc) 19.6 kB
'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 _grammar = require('../grammar/grammar'); var _grammar2 = _interopRequireDefault(_grammar); var _grammarMode = require('../grammar/grammar-mode'); var _lrItem = require('./lr-item'); var _lrItem2 = _interopRequireDefault(_lrItem); var _setsGenerator = require('../sets-generator'); var _setsGenerator2 = _interopRequireDefault(_setsGenerator); var _specialSymbols = require('../special-symbols'); var _debug = require('../debug'); var _debug2 = _interopRequireDefault(_debug); 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 _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * Canonical collection of LR items. * * LR(0) and SLR(1) use collection of LR(0)-items, * LALR(1) and CLR(1) use collection of LR(1)-items. * * The collection is built for a grammar starting from the root * item (for the augmented production), which recursively * applies "closure" and "goto" operations. */ var CanonicalCollection = function () { function CanonicalCollection(_ref) { var grammar = _ref.grammar; _classCallCheck(this, CanonicalCollection); this._grammar = grammar; // Stores transition from a kernel set to an outer state. // This is to reuse the same outer state, if the transition was // already calculated. In this case we just connect to the outer state. this._kernelSetsTransitions = {}; // Stores states by their kernel items LR(0) key. This is to merge // similar states in case of LALR(1) mode. this._lr0ItemSets = {}; // All the states that form this collection. this._states = new Set(); _debug2.default.time('Building canonical collection'); // Root item for the augmented production, "closure" and "goto" // operations applied on this item build the entire collection. this._rootItem = new _lrItem2.default( /* production */this._grammar.getAugmentedProduction(), /* dotPosition */0, /* grammar */this._grammar, /* canonicalCollection */this, /* setsGenerator */new _setsGenerator2.default({ grammar: grammar }), /*lookaheadSet */this._grammar.getMode().usesLookaheadSet() ? _defineProperty({}, _specialSymbols.EOF, true) : null); // Build the entire graph. this._rootItem.closure().goto(); this._remap(); _debug2.default.timeEnd('Building canonical collection'); _debug2.default.log('Number of states in the collection: ' + this._states.size); // LALR(1) by converting to SLR(1), default fast LALR(1) mode. if (this._grammar.getMode().isLALR1()) { this._buildLALRBySLR(); } // LALR(1) by compressing CLR(1): mostly for educational purposes, slow. else if (this._grammar.getMode().isLALR1ByCLR1()) { _debug2.default.time('Compressing CLR to LALR'); this._compressCLRToLALR(); _debug2.default.timeEnd('Compressing CLR to LALR'); _debug2.default.log('Number of states after compression: ' + this._states.size); } } /** * Basic LALR(1) implementation compressing from CLR(1). * * This can be slow on complex grammars, and one can better * use LALR(1) by SLR(1) method. */ _createClass(CanonicalCollection, [{ key: '_compressCLRToLALR', value: function _compressCLRToLALR() { var _this = this; for (var lr0StateKey in this._lr0ItemSets) { var states = this._lr0ItemSets[lr0StateKey]; var rootState = states[0]; rootState.mergeLR0Items(); while (states.length > 1) { var state = states.pop(); state.mergeLR0Items(); rootState.mergeWithState(state); } rootState.getItems().forEach(function (item) { // If the item was already connected, we should recalculate its // connection to the first state in the LR(0) states collection, // since only this state will be kept after states are merged. if (item.isConnected()) { var outerStates = _this.getLR0ItemsSet(item.goto()); var outerState = outerStates[0]; item.connect(outerState); } }); } // After compression reassign new numbers to states. this._remap(); } /** * Builds LALR(1) by SLR(1) grammar, and post-processes LR-items * by calculating needed lookahead sets. * * See good concise explanation of the algorithm here: * https://web.cs.dal.ca/~sjackson/lalr1.html */ }, { key: '_buildLALRBySLR', value: function _buildLALRBySLR() { _debug2.default.time('Building LALR-by-SLR'); this._buildExtendedLALR1Grammar(); this._extendedFollowSets = new _setsGenerator2.default({ grammar: this._extendedLALRGrammar }).getFollowSets(); // Mutate the set with extended symbols to reflect the // symbols from the original grammar. for (var nonTerminal in this._extendedFollowSets) { var set = this._extendedFollowSets[nonTerminal]; for (var symbol in set) { if (this._setsAliasMap.hasOwnProperty(symbol)) { set[this._setsAliasMap[symbol]] = true; delete set[symbol]; } } } this._groupExtendedLALRByFinalSets(); this._updateLALRItemReduceSet(); _debug2.default.timeEnd('Building LALR-by-SLR'); } /** * Groups extended LALR(1) by final sets. * * We merge extended rules, if they are from the same original * rule, and go to the same final set (has the same state number * in the very last symbol of RHS). */ }, { key: '_groupExtendedLALRByFinalSets', value: function _groupExtendedLALRByFinalSets() { var _this2 = this; _debug2.default.time('LALR-by-SLR: Group extended productions by final sets'); this._groupedFinalSets = {}; this._extendedLALRGrammar.getProductions().forEach(function (production) { var LHS = production.getLHS(); var RHS = production.getRHS(); var lastSymbol = RHS[RHS.length - 1]; var originalLHS = LHS.getOrignialSymbol(); var finalSet = lastSymbol.getEndContext(); if (!_this2._groupedFinalSets.hasOwnProperty(finalSet)) { _this2._groupedFinalSets[finalSet] = {}; } if (!_this2._groupedFinalSets[finalSet].hasOwnProperty(originalLHS)) { _this2._groupedFinalSets[finalSet][originalLHS] = {}; } // Merge follow sets. Object.assign(_this2._groupedFinalSets[finalSet][originalLHS], _this2._extendedFollowSets[LHS.getSymbol()]); }); _debug2.default.timeEnd('LALR-by-SLR: Group extended productions by final sets'); } /** * Updates the reduce sets for items in the LALR by SLR algorithm. */ }, { key: '_updateLALRItemReduceSet', value: function _updateLALRItemReduceSet() { var _this3 = this; _debug2.default.time('LALR-by-SLR: Updating item reduce sets'); var states = [].concat(_toConsumableArray(this._states)); var _loop = function _loop(state) { states[state].getReduceItems().forEach(function (reduceItem) { var LHS = reduceItem.getProduction().getLHS().getSymbol(); reduceItem.setReduceSet(_this3._groupedFinalSets[state][LHS]); }); }; for (var state in this._groupedFinalSets) { _loop(state); } _debug2.default.timeEnd('LALR-by-SLR: Updating item reduce sets'); } /** * We use LALR(1) by SLR(1) algorithm here. Once we have built LR(0) * automation, we build the extended grammar, considering the context. * This context further results to needed lookahead set for LALR(1) which * is obtain as Follow(LHS), i.e. the same as in SLR(1). */ }, { key: '_buildExtendedLALR1Grammar', value: function _buildExtendedLALR1Grammar() { _debug2.default.time('LALR-by-SLR: Building extended grammar for LALR'); var extendedBnf = {}; this._setsAliasMap = {}; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = this._states[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var state = _step.value; var items = state.getItems(); var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = items[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var item = _step2.value; // Extended items are built only for beginning items. if (!item.isBeginning()) { continue; } // We traverse the full path of the item, in order to // to identify components with contexts. var current = item; var visited = new Set(); var LHS = item.getProduction().getLHS().getSymbol(); var lhsTransit = state.getTransitionOnSymbol(LHS); var lhsToState = lhsTransit ? lhsTransit.state.getNumber() : _specialSymbols.EOF; var extendedLHSSymbol = state.getNumber() + '|' + LHS + '|' + lhsToState; // Init the rules for the new LHS. if (!extendedBnf.hasOwnProperty(extendedLHSSymbol)) { extendedBnf[extendedLHSSymbol] = []; } var extendedRHS = []; while (current !== null && !visited.has(current)) { visited.add(visited); var transitionSymbol = current.getCurrentSymbol(); if (transitionSymbol) { var rawSymbol = transitionSymbol.getSymbol(); var fromState = current.getState().getNumber(); var toState = void 0; // Epsilon reduces in this state. if (transitionSymbol.isEpsilon()) { toState = fromState; } else if (current.getNext()) { toState = current.getNext().getState().getNumber(); } if (toState != null) { var extendedRHSSymbol = fromState + '|' + rawSymbol + '|' + toState; extendedRHS.push(extendedRHSSymbol); // Collect extended token/terminal symbols as aliases of the // original terminal symbols: this is needed to compute // First/Follow sets as original symbols. if (this._grammar.isTokenSymbol(rawSymbol)) { this._setsAliasMap[extendedRHSSymbol] = rawSymbol; } } } current = current.getNext(); } // Append the new RHS alternative. extendedBnf[extendedLHSSymbol].push(extendedRHS.join(' ')); } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } this._extendedLALRGrammar = new _grammar2.default({ bnf: extendedBnf, mode: _grammarMode.MODES.LALR1_EXTENDED }); _debug2.default.timeEnd('LALR-by-SLR: Building extended grammar for LALR'); } }, { key: 'registerState', value: function registerState(state) { this._states.add(state); // Collect states by LR(0) items, to reuse and merge the same // states in case or LALR(1) mode. var lr0KeyForItems = _lrItem2.default.lr0KeyForItems(state.getKernelItems()); if (!this._lr0ItemSets.hasOwnProperty(lr0KeyForItems)) { this._lr0ItemSets[lr0KeyForItems] = []; } this._lr0ItemSets[lr0KeyForItems].push(state); } }, { key: 'unregisterState', value: function unregisterState(state) { this._states.delete(state); var keyForItems = _lrItem2.default.keyForItems(state.getKernelItems()); delete this._kernelSetsTransitions[keyForItems]; var lr0KeyForItems = _lrItem2.default.lr0KeyForItems(state.getKernelItems()); var lr0States = this._lr0ItemSets[lr0KeyForItems]; var stateIndex = lr0States.indexOf(state); if (stateIndex !== -1) { lr0States.splice(stateIndex, 1); } } }, { key: 'getStates', value: function getStates() { return this._states; } }, { key: 'hasTranstionOnItems', value: function hasTranstionOnItems(items) { return !!this.getTranstionForItems(items); } }, { key: 'getTranstionForItems', value: function getTranstionForItems(items) { return this._kernelSetsTransitions[_lrItem2.default.keyForItems(items)]; } }, { key: 'registerTranstionForItems', value: function registerTranstionForItems(items, outerState) { this._kernelSetsTransitions[_lrItem2.default.keyForItems(items)] = outerState; } /** * In LALR(1) there could be several states with the same * LR(0) items, but which differ only in lookahead symbols. * In this case we merge such states extending their lookaheads. */ }, { key: 'getLR0ItemsSet', value: function getLR0ItemsSet(state) { return this._lr0ItemSets[_lrItem2.default.lr0KeyForItems(state.getKernelItems())]; } }, { key: 'print', value: function print() { var _this4 = this; console.info('\nCanonical collection of LR items:'); this._grammar.print(); this._states.forEach(function (state) { var stateTags = []; if (state.isFinal()) { stateTags.push('final'); if (state.isAccept()) { stateTags.push('accept'); } } console.info('\nState ' + state.getNumber() + ':' + (stateTags.length > 0 ? ' (' + stateTags.join(', ') + ')' : '')); state.getItems().forEach(function (item) { return _this4._printItem(item, state); }); }); } }, { key: '_printItem', value: function _printItem(item, state) { var itemTags = []; if (state.isKernelItem(item)) { itemTags.push('kernel'); } if (item.isShift()) { itemTags.push('shift'); } if (item.isReduce()) { itemTags.push('reduce by production ' + item.getProduction().getNumber()); } if (item.isFinal() && !item.isReduce()) { itemTags.push('accept'); } if (item.goto()) { itemTags.push('goes to state ' + item.goto().getNumber()); } console.info(' - ' + item.toString() + (itemTags.length > 0 ? ' (' + itemTags.join(', ') + ')' : '')); } }, { key: 'getRoot', value: function getRoot() { return this._rootItem; } }, { key: 'getStartingState', value: function getStartingState() { return this.getRoot().getState(); } }, { key: '_remap', value: function _remap() { var number = 0; var _iteratorNormalCompletion3 = true; var _didIteratorError3 = false; var _iteratorError3 = undefined; try { for (var _iterator3 = this._states[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { var state = _step3.value; state.setNumber(number++); } } catch (err) { _didIteratorError3 = true; _iteratorError3 = err; } finally { try { if (!_iteratorNormalCompletion3 && _iterator3.return) { _iterator3.return(); } } finally { if (_didIteratorError3) { throw _iteratorError3; } } } } }]); return CanonicalCollection; }(); exports.default = CanonicalCollection;