UNPKG

syntax-cli-prog

Version:

Syntactic analysis toolkit, language agnostic parsers generator.

346 lines (267 loc) 12.4 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 _canonicalCollection = require('./canonical-collection'); var _canonicalCollection2 = _interopRequireDefault(_canonicalCollection); var _lrParsingTable = require('./lr-parsing-table'); var _lrParsingTable2 = _interopRequireDefault(_lrParsingTable); var _lrParserGeneratorDefault = require('./lr-parser-generator-default'); var _lrParserGeneratorDefault2 = _interopRequireDefault(_lrParserGeneratorDefault); var _tokenizer = require('../tokenizer'); var _tokenizer2 = _interopRequireDefault(_tokenizer); var _specialSymbols = require('../special-symbols'); var _debug = require('../debug'); var _debug2 = _interopRequireDefault(_debug); var _os = require('os'); var _os2 = _interopRequireDefault(_os); var _path = require('path'); var _path2 = _interopRequireDefault(_path); 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"); } } var EntryType = _lrParsingTable2.default.EntryType; var LRParser = function () { function LRParser(_ref) { var grammar = _ref.grammar, parserModule = _ref.parserModule, resolveConflicts = _ref.resolveConflicts; _classCallCheck(this, LRParser); this._grammar = grammar; this._parserModule = parserModule; this._canonicalCollection = new _canonicalCollection2.default({ grammar: this._grammar }); this._table = new _lrParsingTable2.default({ canonicalCollection: this._canonicalCollection, grammar: this._grammar, resolveConflicts: resolveConflicts }); this._tokenizer = new _tokenizer2.default({ lexGrammar: this._grammar.getLexGrammar() }); // Parsing stack. this._stack = []; // Parse object which may define handlers for parse events. this._yyparse = _codeUnit2.default.getSandbox().yyparse; // Global storage accessible from semantic actions. this._yy = _codeUnit2.default.getSandbox().yy; // Parse options. this._yy.options = {}; // Parser may access tokenizer, and affect its state. this._yy.tokenizer = this._tokenizer; // Alias for tokenizer. this._yy.lexer = this._tokenizer; // Whether locations should be captured. this._yy.options.captureLocations = grammar.shouldCaptureLocations(); // Execute module include code which may attach // handlers for some events, and define needed data. _codeUnit2.default.eval(this._grammar.getModuleInclude(), /*shouldRewrite*/false); } _createClass(LRParser, [{ key: 'getGrammar', value: function getGrammar() { return this._grammar; } }, { key: 'getTable', value: function getTable() { return this._table; } }, { key: 'getCanonicalCollection', value: function getCanonicalCollection() { return this._canonicalCollection; } }, { key: 'parse', value: function parse(string) { // If parser module has been generated, use it. if (this._parserModule) { _debug2.default.time('LR parsing from module'); var value = this._parserModule.parse(string); _debug2.default.timeEnd('LR parsing from module'); return { status: 'accept', value: value }; } _debug2.default.time('LR parsing'); this._tokenizer.initString(string); if (this._yyparse.onParseBegin) { this._yyparse.onParseBegin(string, this._tokenizer, this._yy.options); } this._stack = []; var startingState = this._canonicalCollection.getStartingState().getNumber(); // Start from the initial state. this._stack.push(startingState); var token = this._tokenizer.getNextToken(); var shiftedToken = null; do { if (!token) { this._unexpectedEndOfInput(); } var state = this._peek(); var column = token.type; var entry = this._table.get()[state][column]; if (!entry) { this._unexpectedToken(token); } switch (_lrParsingTable2.default.getEntryType(entry)) { case EntryType.SHIFT: if (this._yyparse.onShift) { token = this._yyparse.onShift(token); } this._shift(token, entry); shiftedToken = token; token = this._tokenizer.getNextToken(); break; case EntryType.REDUCE: this._reduce(entry, shiftedToken); // Don't advance tokens on reduce. break; case EntryType.SR_CONFLICT: this._conflictError('shift-reduce', state, column); break; case EntryType.RR_CONFLICT: this._conflictError('reduce-reduce', state, column); break; case EntryType.ACCEPT: { // Pop starting production and its state number. this._stack.pop(); var parsed = this._stack.pop(); if (this._stack.length !== 1 || this._stack[0] !== startingState || this._tokenizer.hasMoreTokens()) { this._unexpectedToken(token); } var result = { status: 'accept' }; if (parsed.hasOwnProperty('semanticValue')) { result.value = parsed.semanticValue; } if (this._yyparse.onParseEnd) { this._yyparse.onParseEnd(result.value); } _debug2.default.timeEnd('LR parsing'); return result; } } } while (this._tokenizer.hasMoreTokens() || this._stack.length > 1); } }, { key: '_unexpectedEndOfInput', value: function _unexpectedEndOfInput() { this._parseError('Unexpected end of input.'); } }, { key: '_unexpectedToken', value: function _unexpectedToken(token) { if (token.type === _specialSymbols.EOF) { this._unexpectedEndOfInput(); } this._tokenizer.throwUnexpectedToken(token.value, token.startLine, token.startColumn); } }, { key: '_parseError', value: function _parseError(message) { throw new SyntaxError(message); } }, { key: '_conflictError', value: function _conflictError(conflictType, state, column) { this._parseError('Found "' + conflictType + '" conflict ' + ('at state ' + state + ', terminal ' + column + '.')); } }, { key: '_peek', value: function _peek() { return this._stack[this._stack.length - 1]; } }, { key: '_shift', value: function _shift(token, entry) { var loc = null; if (this._grammar.shouldCaptureLocations()) { loc = { startOffset: token.startOffset, endOffset: token.endOffset, startLine: token.startLine, endLine: token.endLine, startColumn: token.startColumn, endColumn: token.endColumn }; } this._stack.push({ symbol: token.type, semanticValue: token.value, loc: loc }, Number(entry.slice(1))); } }, { key: '_reduce', value: function _reduce(entry, token) { var productionNumber = entry.slice(1); var production = this._grammar.getProduction(productionNumber); var hasSemanticAction = production.hasSemanticAction(); var semanticValueArgs = hasSemanticAction ? [] : null; var locationArgs = hasSemanticAction && this._grammar.shouldCaptureLocations() ? [] : null; // Pop 2x symbols from the stack (RHS + state number for each), // unless it's an ε-production for which nothing to pop. if (!production.isEpsilon()) { var rhsLengh = production.getRHS().length; while (rhsLengh--) { // Pop state number; this._stack.pop(); // Pop production symbol. var stackEntry = this._stack.pop(); if (hasSemanticAction) { semanticValueArgs.unshift(stackEntry.semanticValue); if (locationArgs) { locationArgs.unshift(stackEntry.loc); } } } } var previousState = this._peek(); var symbolToReduceWith = production.getLHS().getSymbol(); var reduceStackEntry = { symbol: symbolToReduceWith }; if (hasSemanticAction) { _codeUnit2.default.setBindings({ yytext: token ? token.value : '', yyleng: token ? token.value.length : 0 }); var semanticActionArgs = locationArgs !== null ? semanticValueArgs.concat(locationArgs) : semanticValueArgs; // Run corresponding semantic action, result is in $$ (__). production.runSemanticAction(semanticActionArgs); reduceStackEntry.semanticValue = _codeUnit2.default.getSandbox().__; if (locationArgs) { reduceStackEntry.loc = _codeUnit2.default.getSandbox().__loc; } } // Then push LHS. this._stack.push(reduceStackEntry); var nextState = this._table.get()[previousState][symbolToReduceWith]; // And push the next state (goto) this._stack.push(nextState); } }], [{ key: 'fromParserGenerator', value: function fromParserGenerator(_ref2) { var grammar = _ref2.grammar, options = _ref2.options; // Generate parser in the temp directory. var outputFile = _path2.default.resolve(_os2.default.tmpdir(), '.syntax-parser.js'); var parserModule = new _lrParserGeneratorDefault2.default({ grammar: grammar, outputFile: outputFile, options: options }).generate(); return new LRParser({ grammar: grammar, parserModule: parserModule }); } }]); return LRParser; }(); exports.default = LRParser;