UNPKG

syntax-cli-prog

Version:

Syntactic analysis toolkit, language agnostic parsers generator.

274 lines (225 loc) 8.89 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); 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"); } } /** * A lexical grammar rule. */ var LexRule = function () { /** * Format of a rule is: `<matcher> : <tokenType>`. * * In the simplest case a terminal matches itself, and the * token is also the value of the terminal: * * a : "a" * * In other regexp cases, the token can be a class of * tokens, like `NUMBER`: * * [0-9]+(\.[0-9]+)? : NUMBER * * The `startConditions` allows specifying lexer state, and execute * a rule only if a tokenizer is within this state. * * Available options: * * - case-insensitive: boolean */ function LexRule(_ref) { var startConditions = _ref.startConditions, matcher = _ref.matcher, tokenHandler = _ref.tokenHandler, _ref$options = _ref.options, options = _ref$options === undefined ? {} : _ref$options; _classCallCheck(this, LexRule); this._startConditions = startConditions; this._options = options; this._originalMatcher = matcher; this._rawMatcher = this._buildRawMatcher(this._originalMatcher); this._matcher = this._buildMatcher(this._rawMatcher); this._rawHandler = tokenHandler; this._handler = this._buildHandler(tokenHandler); } /** * Returns options. */ _createClass(LexRule, [{ key: 'getOptions', value: function getOptions() { return this._options; } /** * Whether the rule is case-insensitive. */ }, { key: 'isCaseInsensitive', value: function isCaseInsensitive() { return !!this._options['case-insensitive']; } /** * Retusn original matcher string. */ }, { key: 'getOriginalMatcher', value: function getOriginalMatcher() { return this._originalMatcher; } /** * Retusn raw matcher string. */ }, { key: 'getRawMatcher', value: function getRawMatcher() { return this._rawMatcher; } /** * Returns matcher function. */ }, { key: 'getMatcher', value: function getMatcher() { return this._matcher; } /** * Returns raw handler. */ }, { key: 'getRawHandler', value: function getRawHandler() { return this._rawHandler; } /** * Returns handler. */ }, { key: 'getHandler', value: function getHandler() { return this._handler; } /** * Returns token data. */ }, { key: 'getTokenData', value: function getTokenData(matched, tokenizer) { return this._handler(matched, tokenizer); } /** * Returns start conditions for this rule. */ }, { key: 'getStartConditions', value: function getStartConditions() { return this._startConditions; } /** * Whether this rule has start conditions. */ }, { key: 'hasStartConditions', value: function hasStartConditions() { return !!this._startConditions; } /** * Generates a data array like used in lex grammar files * from the LexRule instance. */ }, { key: 'toData', value: function toData() { var data = [this.getOriginalMatcher(), this.getRawHandler()]; if (this.hasStartConditions()) { data.unshift(this.getStartConditions()); } return data; } /** * Tokenizer applies all regexp rules from the beginning of the * non-analized yet substring (i.e. appending ^ to regexp). * * E.g. \d+ becomes ^\d+ * * Note: lookbehind assertions should append ^ not at the beginning * of the regexp, but at the begininng of the asserting chars: * * (?<= )a(?= ) becomes (?<=^ )a(?= ) */ }, { key: '_buildRawMatcher', value: function _buildRawMatcher(originalMatcher) { var prefix = originalMatcher.substring(0, 4); // Positive or negative lookbehind. if (prefix === '(?<=' || prefix === '(?<!') { return prefix + '^' + originalMatcher.slice(4); } // Simple ^. return '^' + originalMatcher; } /** * Creates an actual regexp object from the regexp string (only for JS target * langauges, other plugins use rawMatcher instead ad code generation). */ }, { key: '_buildMatcher', value: function _buildMatcher(rawMatcher) { this._matcher = null; try { var flags = []; if (this._options['case-insensitive']) { flags.push('i'); } this._matcher = new RegExp(rawMatcher, flags.join('')); } catch (e) { /* Skip for other languages */ } return this._matcher; } /** * Builds a wrapper function on top of the handler. * This is in order to be able directly modify `yytext`, * (which is closured), and return a token. */ }, { key: '_buildHandler', value: function _buildHandler(tokenHandler) { try { /* Generate the function handler only for JS language */ var handler = _codeUnit2.default.createHandler( /* no params */'', tokenHandler); return function (matched, tokenizer) { _codeUnit2.default.setBindings({ yytext: matched, yyleng: matched.length }); // Call the handler. var token = handler.call(tokenizer); var yytext = _codeUnit2.default.getSandbox().yytext; // Update the `yyleng` in case `yytext` was modified. _codeUnit2.default.setBindings({ yyleng: yytext.length }); // The handler may mutate `yytext` during execution, // return a possibly updated one, along with the token. return [yytext, token]; }; } catch (e) { /* And skip for other languages, which use raw handler in generator */ } } }], [{ key: 'matcherFromTerminal', value: function matcherFromTerminal(terminal) { return terminal.slice(1, -1).replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); } }]); return LexRule; }(); exports.default = LexRule;