parser-transform
Version:
Streaming+Async lexer and parser
239 lines (216 loc) • 8.01 kB
JavaScript
// 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);