pegisland
Version:
General PEG-based parser supporting island grammars with lake symbols
213 lines • 7.61 kB
JavaScript
"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