pegisland
Version:
General PEG-based parser supporting island grammars with lake symbols
259 lines • 10.7 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GeneralPegBuilder = void 0;
// Copyright (C) 2021- Katsumi Okuda. All rights reserved.
const assert_1 = require("assert");
const ParseTree_1 = require("./ParseTree");
const pe = __importStar(require("./ParsingExpression"));
const Rule_1 = require("./Rule");
const Peg_1 = require("./Peg");
const PegParser_1 = require("./PegParser");
const set_operations_1 = require("./set-operations");
class GeneralPegBuilder {
constructor() {
this.rules = new Map();
this.visitedRules = new Set();
this.errors = [];
}
build(grammar) {
const pegParser = new PegParser_1.PegParser();
const result = pegParser.parse(grammar);
if (result instanceof Error) {
return result;
}
this.makeRules(result);
const toplevelRules = (0, set_operations_1.difference)(new Set(this.rules.values()), this.visitedRules);
if (this.errors.length !== 0) {
return Error(this.errors.join('\n'));
}
return new Peg_1.Peg(this.rules, [...toplevelRules]);
}
makeRules(tree) {
const [seq] = tree.childNodes;
const [, plus] = seq.childNodes;
plus.childNodes.forEach((node) => {
const [seq] = node.childNodes;
const [id] = seq.childNodes[1].childNodes[0].childNodes;
(0, assert_1.strict)(id instanceof ParseTree_1.NodeTerminal);
const rule = this.getRule(id.text);
rule.rhs = this.processExpression(seq.childNodes[3]);
// check if it has a water annotation
const optAnnotations = seq.childNodes[0];
const [annotations] = optAnnotations.childNodes;
const hasAnnotation = annotations.childNodes.length > 0;
rule.isWater = hasAnnotation;
});
}
getRule(symbol) {
if (!this.rules.has(symbol)) {
this.rules.set(symbol, new Rule_1.Rule(symbol, new pe.NullParsingExpression()));
}
return this.rules.get(symbol);
}
processExpression(node) {
(0, assert_1.strict)(node instanceof ParseTree_1.NodeNonterminal);
const [seq] = node.childNodes;
(0, assert_1.strict)(seq instanceof ParseTree_1.NodeSequence);
const [rewriting, star] = seq.childNodes;
const rewritings = [rewriting];
star.childNodes.forEach((seq) => {
const [, rewriting] = seq.childNodes;
rewritings.push(rewriting);
});
const operands = rewritings.map((rewriting) => this.processRewriting(rewriting));
return operands.length === 1 ? operands[0] : new pe.OrderedChoice(operands);
}
processRewriting(node) {
(0, assert_1.strict)(node instanceof ParseTree_1.NodeNonterminal);
const [seq] = node.childNodes;
const [sequence, opt] = seq.childNodes;
let operand = this.processSequence(sequence);
if (opt.childNodes.length === 1) {
const [seq] = opt.childNodes;
const [, str] = seq.childNodes;
const [choice] = str.childNodes[0].childNodes;
const [term] = choice.childNodes;
(0, assert_1.strict)(term instanceof ParseTree_1.NodeTerminal);
const result = eval(term.text);
operand = new pe.Rewriting(operand, result);
}
return operand;
}
processSequence(node) {
(0, assert_1.strict)(node instanceof ParseTree_1.NodeNonterminal);
const [star] = node.childNodes;
const operands = star.childNodes.map((node) => this.processPrefix(node.childNodes[0]));
return operands.length === 1 ? operands[0] : new pe.Sequence(operands);
}
processPrefix(node) {
(0, assert_1.strict)(node instanceof ParseTree_1.NodeNonterminal);
const [seq] = node.childNodes;
const [opt, suffix] = seq.childNodes;
let operand = this.processSuffix(suffix);
if (opt.childNodes.length !== 0) {
const choice = opt.childNodes[0];
operand = new [pe.And, pe.Not][choice.index](operand);
}
return operand;
}
processSuffix(node) {
(0, assert_1.strict)(node instanceof ParseTree_1.NodeNonterminal);
const [seq] = node.childNodes;
const [primary, opt] = seq.childNodes;
let operand = this.processPrimary(primary);
if (opt.childNodes.length !== 0) {
const choice = opt.childNodes[0];
operand = [
() => this.makeSuffixWithOperand(choice, operand),
() => this.makeSuffixWithOperand(choice, operand),
() => this.makeSuffixWithOperand(choice, operand),
() => this.makeSuffixWithOperand(choice, operand),
() => new pe.Optional(operand),
() => new pe.ZeroOrMore(operand),
() => new pe.OneOrMore(operand),
][choice.index]();
}
return operand;
}
makeSuffixWithOperand(choice, operand) {
const [choiceOperand] = choice.childNodes;
const colonSeq = choiceOperand;
const [, rhs] = colonSeq.childNodes;
const rhsOperand = this.processPrimary(rhs);
return [
() => createStarPlus(operand, rhsOperand),
() => createPlusPlus(operand, rhsOperand),
() => new pe.ColonNot(operand, rhsOperand),
() => new pe.Colon(operand, rhsOperand),
][choice.index]();
}
processPrimary(node) {
(0, assert_1.strict)(node instanceof ParseTree_1.NodeNonterminal);
const choice = node.childNodes[0];
(0, assert_1.strict)(choice.index <= 6);
return [
this.processRegexp,
this.processLake,
this.processNamedIdentifier,
this.processGrouping,
this.processString,
this.processClass,
this.processDot,
][choice.index].call(this, choice.childNodes[0]);
}
processOperatorWithOneOperand(node, PeCtor) {
(0, assert_1.strict)(node instanceof ParseTree_1.NodeSequence);
const operand = this.processExpression(node.childNodes[1]);
return new PeCtor(operand);
}
processGrouping(node) {
return this.processOperatorWithOneOperand(node, pe.Grouping);
}
processLake(node) {
return this.processOperatorWithOneOperand(node, pe.Lake);
}
processRegexp(node) {
(0, assert_1.strict)(node instanceof ParseTree_1.NodeNonterminal);
const [seq] = node.childNodes;
const [choice] = seq.childNodes;
const [term] = choice.childNodes;
(0, assert_1.strict)(term instanceof ParseTree_1.NodeTerminal);
const { text } = term;
const pattern = text.slice(2, text.length - 1);
const regex = this.createRegExp(pattern, node);
return new pe.Terminal(regex, text);
}
createRegExp(pattern, node) {
try {
return new RegExp(pattern);
}
catch (e) {
const { column } = node.range.start;
const { line } = node.range.start;
this.errors.push(`Invalid regular expression: ${pattern} at ${line}:${column}`);
return /./;
}
}
processNamedIdentifier(node) {
(0, assert_1.strict)(node instanceof ParseTree_1.NodeNonterminal);
const [seq] = node.childNodes;
const [opt] = seq.childNodes;
(0, assert_1.strict)(opt instanceof ParseTree_1.NodeOptional);
let subname = '';
if (opt.childNodes.length > 0) {
const optSeq = opt.childNodes[0];
const term = optSeq.childNodes[0];
subname = term.text;
}
const id = seq.childNodes[1];
{
const [seq] = id.childNodes;
const [term] = seq.childNodes;
(0, assert_1.strict)(term instanceof ParseTree_1.NodeTerminal);
const rule = this.getRule(term.text);
this.visitedRules.add(rule);
return new pe.Nonterminal(rule, subname);
}
}
processString(node) {
(0, assert_1.strict)(node instanceof ParseTree_1.NodeNonterminal);
const [seq] = node.childNodes;
const [choice] = seq.childNodes;
const [term] = choice.childNodes;
(0, assert_1.strict)(term instanceof ParseTree_1.NodeTerminal);
const result = eval(term.text);
const pattern = result.replace(/([^0-9a-zA-Z])/g, '\\$1');
return new pe.Terminal(this.createRegExp(pattern, node), term.text);
}
processClass(node) {
(0, assert_1.strict)(node instanceof ParseTree_1.NodeNonterminal);
const [seq] = node.childNodes;
const [terminal] = seq.childNodes;
(0, assert_1.strict)(terminal instanceof ParseTree_1.NodeTerminal);
const text = terminal.text[0] === '^'
? `[^${terminal.text.substring(2)}`
: terminal.text;
return new pe.Terminal(this.createRegExp(text, node), terminal.text);
}
processDot(node) {
(0, assert_1.strict)(node instanceof ParseTree_1.NodeNonterminal);
return new pe.Terminal(/./, '.');
}
}
exports.GeneralPegBuilder = GeneralPegBuilder;
function createXPlus(lhs, rhs, PeCtor) {
return new pe.Sequence([
new PeCtor(new pe.Grouping(new pe.Sequence([new pe.Not(rhs), lhs]))),
rhs,
]);
}
function createPlusPlus(lhs, rhs) {
return createXPlus(lhs, rhs, pe.OneOrMore);
}
function createStarPlus(lhs, rhs) {
return createXPlus(lhs, rhs, pe.ZeroOrMore);
}
//# sourceMappingURL=GeneralPegBuilder.js.map