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