UNPKG

syntax-cli-prog

Version:

Syntactic analysis toolkit, language agnostic parsers generator.

394 lines (316 loc) 11.3 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 _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;