syntax-cli-prog
Version:
Syntactic analysis toolkit, language agnostic parsers generator.
338 lines (272 loc) • 11.8 kB
JavaScript
;
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;