UNPKG

syntax-cli-prog

Version:

Syntactic analysis toolkit, language agnostic parsers generator.

338 lines (272 loc) 11.8 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); 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 _lexRule = require('./lex-rule'); var _lexRule2 = _interopRequireDefault(_lexRule); var _specialSymbols = require('../special-symbols'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * Standard macro symbols. */ var StandardMacros = { /** * End of file macro, matches `$` at the end of the parsing string. */ '<<EOF>>': _specialSymbols.EOF }; /** * Class encapsulates operations with a lexical grammar. */ var LexGrammar = function () { /** * A lexical grammar is used for a string tokenization. An example of the * lexical grammar data: * * { * "macros": { * "digit": "[0-9]", * }, * * "rules": [ * ["a", "return 'a';"], * ["\\(", "return '(';"], * ["\\)", "return ')';"], * ["\\+", "return '+';"], * ["{digit}+(\\.{digit}+)?\\b", "return 'NUMBER';"], * * // A rule with start conditions. Such rules are matched only * // when a scanner enters these states. * [["string", "code"], '[^"]', "return 'STRING';"], * ], * * // https://gist.github.com/DmitrySoshnikov/f5e2583b37e8f758c789cea9dcdf238a * "startConditions": { * "string": 1, // inclusive condition %s * "code": 0, // exclusive consition %x * }, * } */ function LexGrammar(_ref) { var macros = _ref.macros, rules = _ref.rules, startConditions = _ref.startConditions, options = _ref.options; _classCallCheck(this, LexGrammar); this._macros = macros; this._originalRules = rules; this._options = options; this._extractMacros(macros, this._originalRules); this._rules = this._processRules(this._originalRules); this._rulesToIndexMap = this._createRulesToIndexMap(); this._startConditions = Object.assign({ INITIAL: 0 }, startConditions); this._rulesByStartConditions = this._processRulesByStartConditions(); } /** * Returns options. */ _createClass(LexGrammar, [{ key: 'getOptions', value: function getOptions() { return this._options; } /** * Returns start conditions types for a lexer. */ }, { key: 'getStartConditions', value: function getStartConditions() { return this._startConditions; } /** * Returns lexical rules. */ }, { key: 'getRules', value: function getRules() { return this._rules; } /** * Returns a rule by index. */ }, { key: 'getRuleByIndex', value: function getRuleByIndex(index) { return this._rules[index]; } /** * Returns rule's index. */ }, { key: 'getRuleIndex', value: function getRuleIndex(rule) { return this._rulesToIndexMap.get(rule); } /** * Returns original lexical rules data. */ }, { key: 'getOriginalRules', value: function getOriginalRules() { return this._originalRules; } /** * Returns macros. */ }, { key: 'getMacros', value: function getMacros() { return this._macros; } /** * Returns lexical rules for a specific start condition. */ }, { key: 'getRulesForState', value: function getRulesForState(state) { return this._rulesByStartConditions[state]; } /** * Returns rules by start conditions. */ }, { key: 'getRulesByStartConditions', value: function getRulesByStartConditions() { return this._rulesByStartConditions; } /** * Creates rules to index map. */ }, { key: '_createRulesToIndexMap', value: function _createRulesToIndexMap() { var rulesToIndexMap = new Map(); this.getRules().forEach(function (rule, index) { rulesToIndexMap.set(rule, index); }); return rulesToIndexMap; } /** * Processes lexical rules data, creating `LexRule` instances for each. */ }, { key: '_processRules', value: function _processRules(rules) { var _this = this; return rules.map(function (tokenData) { // Lex rules may specify start conditions. Such rules are // executed if a tokenizer enters such state. var startConditions = void 0; var matcher = void 0; var tokenHandler = void 0; var options = {}; // Default options of a particular LexRule are initialized to the // global options of the whole lexical grammar. var defaultOptions = Object.assign({}, _this.getOptions()); if (tokenData.length === 2) { var _tokenData = _slicedToArray(tokenData, 2); matcher = _tokenData[0]; tokenHandler = _tokenData[1]; } else if (tokenData.length === 3) { // Start conditions, no options. if (Array.isArray(tokenData[0]) && typeof tokenData[2] === 'string') { var _tokenData2 = _slicedToArray(tokenData, 3); startConditions = _tokenData2[0]; matcher = _tokenData2[1]; tokenHandler = _tokenData2[2]; } // Trailing options, no start conditions. else if (typeof tokenData[0] === 'string' && typeof tokenData[2] === 'object') { var _tokenData3 = _slicedToArray(tokenData, 3); matcher = _tokenData3[0]; tokenHandler = _tokenData3[1]; options = _tokenData3[2]; } } else if (tokenData.length === 4) { var _tokenData4 = _slicedToArray(tokenData, 4); startConditions = _tokenData4[0]; matcher = _tokenData4[1]; tokenHandler = _tokenData4[2]; options = _tokenData4[3]; } return new _lexRule2.default({ startConditions: startConditions, matcher: matcher, tokenHandler: tokenHandler, options: Object.assign(defaultOptions, options) }); }); } /** * Builds a map from a start condition to a list of * lex rules which should be executed once a lexer * enters this state. */ }, { key: '_processRulesByStartConditions', value: function _processRulesByStartConditions() { var _this2 = this; var rulesByConditions = {}; var _loop = function _loop(condition) { var inclusive = _this2._startConditions[condition] === 0; var rules = _this2._rules.filter(function (lexRule) { // A rule is included if a lexer is in this state, // or if a condition is inclusive, and a rule doesn't have // any explicit start conditions. Also if the condition is `*`. // https://gist.github.com/DmitrySoshnikov/f5e2583b37e8f758c789cea9dcdf238a return inclusive && !lexRule.hasStartConditions() || lexRule.hasStartConditions() && (lexRule.getStartConditions().indexOf(condition) !== -1 || lexRule.getStartConditions().indexOf('*') !== -1); }); rulesByConditions[condition] = rules; }; for (var condition in this._startConditions) { _loop(condition); } return rulesByConditions; } /** * If lexical grammar provides "macros" property, and has e.g entry: * "digit": "[0-9]", with later usage of {digit} in the lex rules, * this functions expands it to [0-9]. */ }, { key: '_extractMacros', value: function _extractMacros(macros, rules) { rules.forEach(function (lexData) { var index = lexData.length === 3 ? 1 : 0; // Standard macros. var _loop2 = function _loop2(macro) { if (lexData[index].indexOf(macro) !== -1) { lexData[index] = lexData[index].replace(new RegExp(macro, 'g'), function () { return StandardMacros[macro]; }); } }; for (var macro in StandardMacros) { _loop2(macro); } if (!macros) { return; } var _loop3 = function _loop3(macro) { // User-level macros. if (lexData[index].indexOf('{' + macro + '}') !== -1) { lexData[index] = lexData[index].replace(new RegExp('\\{' + macro + '\\}', 'g'), function () { return macros[macro]; }); } }; for (var macro in macros) { _loop3(macro); } }); } }]); return LexGrammar; }(); exports.default = LexGrammar;