UNPKG

pegisland

Version:

General PEG-based parser supporting island grammars with lake symbols

213 lines 7.61 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BottomUpParser = exports.BottomUpParserBase = exports.getTopLevelExpressions = exports.isGrowing = exports.BottomUpParsingEnv = void 0; // Copyright (C) 2022- Katsumi Okuda. All rights reserved. const assert_1 = require("assert"); const line_column_1 = __importDefault(require("line-column")); const BeginningCalculator_1 = require("./set/BeginningCalculator"); const GraphBuilder_1 = require("./GraphBuilder"); const Indexer_1 = require("./Indexer"); const ParsingExpression_1 = require("./ParsingExpression"); const IParsingEnv_1 = require("./IParsingEnv"); const Position_1 = require("./Position"); const PriorityQueue_1 = require("./PriorityQueue"); class BottomUpParsingEnv extends IParsingEnv_1.BaseParsingEnv { constructor(s, peg) { super(s); this.peg = peg; const [parentsMap, childrenMap] = createParentsMap(this.peg); // console.log(genDot(peg, parentsMap)); this.parentsMap = parentsMap; this.createHeap = getHeapCreator(this.peg, childrenMap); } parseString(s, start) { this.fillMemoTable(s); const startRule = this.peg.rules.get(start); if (!startRule) { return Error(`${start} is not a valid nonterminal symbol.`); } const result = this.parseRule(startRule, new Position_1.Position(0, 1, 1)); if (result === null) { return Error(`Failed to recognize ${start}`); } return result; } parseRule(rule, pos) { if (!this.memo[pos.offset].has(rule)) { // console.log('XXX: ', pos.offset, rule.symbol); this.memo[pos.offset].set(rule, null); } return this.memo[pos.offset].get(rule); } parse(pe, pos) { return pe.accept(this.recognizer, pos); } fillMemoTable(s) { const dummy = ' '; const finder = (0, line_column_1.default)(s + dummy); const makePos = (index) => { const info = finder.fromIndex(index); (0, assert_1.strict)(info !== null); return new Position_1.Position(index, info.line, info.col); }; for (let pos = s.length; pos >= 0; pos--) { this.fillMemoEntry(makePos, pos); } } fillMemoEntry(makePos, pos) { const [heap] = this.createHeap(); // console.log('heap was created for ' + pos, heap.size()); while (!heap.empty()) { const rule = heap.pop(); const isGrowing = this.grow(rule, makePos(pos)); /* if (isGrowing) console.log( pos, heap.size(), peToString(pe) + ' was popped!' + (this.memo[pos].get(pe) !== null) ); */ if (!isGrowing) continue; const parents = this.parentsMap.get(rule); if (!parents) continue; parents.forEach((parent) => heap.push(parent)); } } grow(rule, pos) { const isFirstEval = !this.memo[pos.offset].has(rule); // console.log('Grow ' + show(pe)); if (isFirstEval) { this.memo[pos.offset].set(rule, null); } const result = rule.parse(this, pos); const oldResult = this.memo[pos.offset].get(rule); if (isFirstEval || isGrowing(result, oldResult)) { this.memo[pos.offset].set(rule, result); // console.log(result); return true; } return false; } } exports.BottomUpParsingEnv = BottomUpParsingEnv; function isGrowing(result, oldResult) { if (oldResult === null) { // console.log('oldResult is null'); return result !== null; } (0, assert_1.strict)(result !== null, "The result can't be null once it was not null"); const [, pos] = result; const [, oldPos] = oldResult; // console.log(pos.offset, oldPos.offset); return pos.offset > oldPos.offset; } exports.isGrowing = isGrowing; function getTopLevelExpressions(peg) { return getTopLevelRules(peg).map((rule) => rule.rhs); } exports.getTopLevelExpressions = getTopLevelExpressions; function getTopLevelRules(peg) { return peg.toplevelRules.length > 0 ? peg.toplevelRules : [peg.rules.values().next().value]; } class DFSTraverser { constructor(childrenMap, topLevelRules) { this.childrenMap = childrenMap; this.topLevelRules = topLevelRules; this.visited = new Set(); this.bottoms = new Set(); } visit(pe) { const children = this.childrenMap.has(pe) ? [...this.childrenMap.get(pe)] : []; const unvisitedChildren = children.filter((child) => !this.visited.has(child)); if (unvisitedChildren.length === 0) { this.bottoms.add(pe); } else { unvisitedChildren.forEach((child) => { this.visited.add(child); this.visit(child); }); } } traverse() { this.topLevelRules.forEach((rule) => { this.visited.add(rule); this.visit(rule); }); } } function getHeapCreator(peg, childrenMap) { const indexer = new Indexer_1.Indexer(); const [indexMap] = indexer.build(peg); (0, assert_1.strict)(peg.toplevelRules.length > 0, 'One or more top-level rules are needed.'); const traverser = new DFSTraverser(childrenMap, peg.toplevelRules); traverser.traverse(); const calculator = new BeginningCalculator_1.BeginningCalculator(peg.rules, true).calculate(); const beginWithTerminal = (rule) => [...calculator.get(rule.rhs).values()].filter((pe) => pe instanceof ParsingExpression_1.Terminal).length > 0; const bottomRules = [...peg.rules.values()].filter(beginWithTerminal); /* console.log( 'XXX', [...difference(new Set(bottomRules), bottoms)] .map((rule) => rule.symbol) .join(',') ); console.log( 'YYY', [...difference(bottoms, new Set(bottomRules))] .map((rule) => rule.symbol) .join(',') ); */ /* console.log( 'bottoms', [...bottomRules].map((rule) => peToString(rule.rhs)).join('\n') ); */ const cmp = (a, b) => indexMap.get(a.rhs) - indexMap.get(b.rhs); return () => { const heap = new PriorityQueue_1.PriorityQueue(cmp); bottomRules.forEach((rule) => heap.push(rule)); // bottoms.forEach((pe) => heap.push(pe)); return [heap, indexMap]; }; } function createParentsMap(peg) { const parentsBuilder = new GraphBuilder_1.GraphBuilder(); const parentsMap = parentsBuilder.build(peg); return parentsMap; } class BottomUpParserBase { constructor(peg, ParsingEnvCtor) { this.peg = peg; this.ParsingEnvCtor = ParsingEnvCtor; } parse(s, start) { const env = new this.ParsingEnvCtor(s, this.peg); const result = env.parseString(s, start ? start : this.peg.rules.keys().next().value); if (result instanceof Error) { return result; } const [tree] = result; return tree; } } exports.BottomUpParserBase = BottomUpParserBase; class BottomUpParser extends BottomUpParserBase { constructor(peg) { super(peg, BottomUpParsingEnv); } } exports.BottomUpParser = BottomUpParser; //# sourceMappingURL=BottomUpParser.js.map