UNPKG

pegisland

Version:

General PEG-based parser supporting island grammars with lake symbols

133 lines 4.52 kB
"use strict"; 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