pegisland
Version:
General PEG-based parser supporting island grammars with lake symbols
133 lines • 4.52 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.createParser = exports.Parser = exports.parseGrammar = void 0;
// Copyright (C) 2021- Katsumi Okuda. All rights reserved.
const BottomUpParser_1 = require("./BottomUpParser");
const FirstCalculator_1 = require("./set/FirstCalculator");
const GeneralPegBuilder_1 = require("./GeneralPegBuilder");
const lake_1 = require("./lake");
const PackratParser_1 = require("./PackratParser");
const ParsingExpression_1 = require("./ParsingExpression");
const PostorderExpressionTraverser_1 = require("./PostorderExpressionTraverser");
const Stats_1 = require("./Stats");
const utils_1 = require("./utils");
function parseGrammar(grammar) {
const builder = new GeneralPegBuilder_1.GeneralPegBuilder();
const result = builder.build(grammar);
if (result instanceof Error) {
return result;
}
const errors = [...result.rules.values()]
.filter((rule) => rule.rhs instanceof ParsingExpression_1.NullParsingExpression)
.filter((rule) => !(0, lake_1.isLake)(rule.symbol))
.map((rule) => new Error(`Rule '${rule.symbol}' is not defined.`));
if (errors.length > 0) {
return errors[0];
}
return result;
}
exports.parseGrammar = parseGrammar;
function isLeftRecursive(peg) {
const firstSets = new FirstCalculator_1.FirstCalculator(peg.rules).calculate();
return [...peg.rules.entries()].some(([symbol, rule]) => [...firstSets.get(rule.rhs)].some((pe) => pe instanceof ParsingExpression_1.Nonterminal && pe.rule.symbol === symbol));
}
class Parser {
constructor(peg, stats = new Stats_1.Stats()) {
this.stats = stats;
if (isLeftRecursive(peg)) {
stats.grammarInfo.isLeftRecursive = true;
this.pegInterpreter = new BottomUpParser_1.BottomUpParser(peg);
}
else {
this.pegInterpreter = new PackratParser_1.PackratParser(peg.rules);
}
}
parse(s, startSymbol) {
const [result, time] = (0, utils_1.measure)(() => this.pegInterpreter.parse(s, startSymbol, this.stats));
this.stats.parsingTime += time;
this.stats.totalTextLength += s.length;
return result;
}
}
exports.Parser = Parser;
function createParser(grammar, waterSymbols = ['water']) {
const stats = new Stats_1.Stats();
// Parse a grammar specification
const [peg, grammarConstructionTime] = (0, utils_1.measure)(() => parseGrammar(grammar));
if (peg instanceof Error) {
return peg;
}
stats.grammarConstructionTime = grammarConstructionTime;
// Summarize the grammar
analyzeGrammar(peg, stats.grammarInfo);
if (stats.grammarInfo.lakeCount > 0 ||
stats.grammarInfo.lakeSymbolCount > 0) {
// Process lakes
const [, time] = (0, utils_1.measure)(() => (0, lake_1.processLakes)(peg, waterSymbols));
stats.lakeProcessingTime = time;
}
// Create a parser
const parser = new Parser(peg, stats);
return parser;
}
exports.createParser = createParser;
class Counter {
constructor(info) {
this.info = info;
}
visitNonterminal(pe) {
this.info.nonterminalCount++;
if ((0, lake_1.isLake)(pe.rule.symbol)) {
this.info.lakeSymbolCount++;
}
}
visitTerminal(_pe) {
this.info.terminalCount++;
}
visitAnd(_pe) {
this.info.andCount++;
}
visitNot(_pe) {
this.info.notCount++;
}
visitColon(_pe) {
this.info.colonCount++;
}
visitColonNot(_pe) {
this.info.colonNotCount++;
}
visitGrouping(_pe) {
this.info.groupingCount++;
}
visitLake(_pe) {
this.info.lakeCount++;
}
visitZeroOrMore(_pe) {
this.info.zeroOrMoreCount++;
}
visitOneOrMore(_pe) {
this.info.oneOrMoreCount++;
}
visitOptional(_pe) {
this.info.optionalCount++;
}
visitOrderedChoice(_pe) {
this.info.orderedChoiceCount++;
}
visitRewriting(_pe) {
this.info.rewritingCount++;
}
visitSequence(_pe) {
this.info.sequenceCount++;
}
}
function analyzeGrammar(peg, info) {
const traverser = new PostorderExpressionTraverser_1.PostorderExpressionTraverser(new Counter(info));
peg.rules.forEach((rule) => {
if (!(rule.rhs instanceof ParsingExpression_1.NullParsingExpression)) {
info.ruleCount++;
traverser.traverse(rule.rhs);
}
});
}
//# sourceMappingURL=Parser.js.map