UNPKG

parser-transform

Version:

Streaming+Async lexer and parser

239 lines (216 loc) 8.01 kB
// Generated by CoffeeScript 2.3.2 (function() { var CanonicalCollection, EntryType, Grammar, LRParser, LRParserGenerator, LRParsingTable, ParserTransform, SpecialSymbols, Stream, StreamingLRParser, StreamingLRParserGenerator, vm; ({LRParser, LRParsingTable, Grammar, CanonicalCollection, LRParserGenerator} = require('syntax-cli')); ({EntryType} = LRParsingTable); SpecialSymbols = require('syntax-cli/dist/special-symbols.js'); Stream = require('stream'); vm = require('vm'); StreamingLRParserGenerator = class StreamingLRParserGenerator extends LRParserGenerator { constructor({grammar, options = {}}) { var context; super({grammar, options}); this.context = { yytext: '', yyleng: 0, __: null, __handler: [] }; context = vm.createContext(this.context); this.vm = { eval: function(code) { return vm.runInContext(code, context); } }; return; } // This is in `traits`. buildSemanticAction(production) { var action, args, handler, index, name; // action is the text action = this.getSemanticActionCode(production); if (!action) { return null; } // args is the Array containing the argument names args = this.getSemanticActionParams(production); // Save the action, they are injected later. // "__handler[1]", "__handler[2]", etc. index = production.getNumber(); name = `__handler[${index}]`; handler = this.vm.eval(`__handler[${index}] = async function (${args}) {\n${action}\n}`); return name; } generateModuleInclude() { var moduleInclude; moduleInclude = this._grammar.getModuleInclude(); if (moduleInclude != null) { this.vm.eval(moduleInclude); } } }; StreamingLRParser = (function() { // Replaces the "template" class StreamingLRParser { constructor(generator) { this._generator = generator; this._grammar = generator._grammar; this._table = generator.getTable(); this._stack = []; this._startingState = this._table._canonicalCollection.getStartingState().getNumber(); this._stack.push(this._startingState); this._shiftedToken = null; return; } async _reduce(entry, token) { var handler, hasSemanticAction, locationArgs, nextState, previousState, production, productionNumber, reduceStackEntry, rhsLengh, semanticActionArgs, semanticValueArgs, stackEntry, symbolToReduceWith; productionNumber = entry.slice(1); production = this._grammar.getProduction(productionNumber); hasSemanticAction = production.hasSemanticAction(); semanticValueArgs = hasSemanticAction ? [] : null; 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()) { rhsLengh = production.getRHS().length; while (rhsLengh--) { // Pop state number this._stack.pop(); // Pop production symbol. stackEntry = this._stack.pop(); if (hasSemanticAction) { semanticValueArgs.unshift(stackEntry.semanticValue); if (locationArgs) { locationArgs.unshift(stackEntry.loc); } } } } previousState = this._peek(); symbolToReduceWith = production.getLHS().getSymbol(); reduceStackEntry = { symbol: symbolToReduceWith }; if (hasSemanticAction) { this._generator.context.yytext = token ? token.value : ''; this._generator.context.yyleng = token ? token.value.length : 0; semanticActionArgs = locationArgs !== null ? semanticValueArgs.concat(locationArgs) : semanticValueArgs; // Run corresponding semantic action, result is in $$ (__). handler = this._generator.context.__handler[productionNumber]; await handler(...semanticActionArgs).catch(console.error); reduceStackEntry.semanticValue = this._generator.context.__; if (locationArgs) { reduceStackEntry.loc = CodeUnit.getSandbox().$$loc; } } // Then push LHS. this._stack.push(reduceStackEntry); nextState = this._table.get()[previousState][symbolToReduceWith]; // And push the next state (goto) this._stack.push(nextState); } }; StreamingLRParser.prototype._peek = LRParser.prototype._peek; StreamingLRParser.prototype._shift = LRParser.prototype._shift; return StreamingLRParser; }).call(this); ParserTransform = class ParserTransform extends Stream.Transform { constructor(grammar, options = {}) { var generator; options.objectMode = true; super(options); generator = new StreamingLRParserGenerator({grammar}); generator.generateParserData(); generator.context.emit = (chunk) => { return this.push(chunk); }; this.parser = new StreamingLRParser(generator); return; } _transform(chunk, encoding, next) { var column, entry, parsed, result, state, token; token = { type: chunk.eof ? SpecialSymbols.EOF : chunk.token, value: chunk.text, line: chunk.line, column: chunk.column }; if (!(this.parser._stack.length >= 1)) { next(new Error("Parser stack too small")); return; } state = this.parser._peek(); column = token.type; entry = this.parser._table.get()[state][column]; if (!entry) { next(new Error(`Unexpected token ${column} in state ${state} at line ${token.line} column ${token.column}`)); return; } switch (LRParsingTable.getEntryType(entry)) { case EntryType.SHIFT: this.parser._shift(token, entry); this.parser._shiftedToken = token; next(); break; case EntryType.REDUCE: (async() => { var error; try { await this.parser._reduce(entry, this.parser._shiftedToken); // Don't advance tokens on reduce. return this._transform(chunk, encoding, next); } catch (error1) { error = error1; next(error); } })(); break; case EntryType.SR_CONFLICT: next(new Error(`ConflictError: shift-reduce in ${state}, ${column}`)); break; case EntryType.RR_CONFLICT: next(new Error(`ConflictError: reduce-reduce in ${state}, ${column}`)); break; case EntryType.ACCEPT: // Pop starting production and its state number. this.parser._stack.pop(); parsed = this.parser._stack.pop(); if (!(this.parser._stack.length === 1 && this.parser._stack[0] === this.parser._startingState && chunk.eof)) { next(new Error(`Unexpected token ${token}`)); return; } result = { status: 'accept' }; if (parsed.hasOwnProperty('semanticValue')) { result.value = parsed.semanticValue; } this.emit('success', result); next(); } } _final(next) { (async() => { var error; error = null; try { while (this.parser._stack.length > 1) { await new Promise((resolve) => { return this._transform({ eof: true }, null, resolve); }); } } catch (error1) { error = error1; } if (error != null) { next(error); } else { next(); } })(); } }; module.exports = ParserTransform; }).call(this);