@fauton/cfg
Version:
A package to work with context free grammars and LL1 parsers
83 lines (82 loc) • 5.05 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.removeNonDeterminism = void 0;
const populateCfg_1 = require("./utils/populateCfg");
/**
* Left factors a grammar to remove non determinism
* @param inputCfg Input context free grammar
* @returns Left refactored grammar
*/
function removeNonDeterminism(inputCfg) {
const cfg = (0, populateCfg_1.populateCfg)(inputCfg);
const cfgProductionRulesEntries = Object.entries(cfg.productionRules);
let currentEntryNumber = 0;
while (currentEntryNumber !== cfgProductionRulesEntries.length) {
const [productionVariable, productionRules] = cfgProductionRulesEntries[currentEntryNumber];
// Production rule index and number of tokens that matched, index with 0 matched tokens are not added
const ruleIndexMatchedTokensCountRecord = {};
for (let outerProductionRuleIndex = 0; outerProductionRuleIndex < productionRules.length - 1; outerProductionRuleIndex += 1) {
for (let innerProductionRuleIndex = outerProductionRuleIndex + 1; innerProductionRuleIndex < productionRules.length; innerProductionRuleIndex += 1) {
const outerProductionRuleTokens = productionRules[outerProductionRuleIndex].split(" ");
const innerProductionRuleTokens = productionRules[innerProductionRuleIndex].split(" ");
let totalTokenMatches = 0;
let currentTokenIndex = 0;
while (currentTokenIndex !== innerProductionRuleTokens.length) {
if (outerProductionRuleTokens[currentTokenIndex] === innerProductionRuleTokens[currentTokenIndex]) {
totalTokenMatches += 1;
ruleIndexMatchedTokensCountRecord[outerProductionRuleIndex] = totalTokenMatches;
}
else {
// Break the loop if there is mismatch between two tokens in the same index
break;
}
currentTokenIndex += 1;
}
// Only populate record if there is at least one token match
if (totalTokenMatches !== 0) {
ruleIndexMatchedTokensCountRecord[innerProductionRuleIndex] = totalTokenMatches;
}
}
}
// Only continue if there is common tokens
if (Object.keys(ruleIndexMatchedTokensCountRecord).length !== 0) {
// Get the least number of common token
const minTokenMatches = Math.min(...Object.values(ruleIndexMatchedTokensCountRecord));
const newProductionVariable = `${productionVariable}'`;
const productionRulesForNewVariable = [];
const productionRulesForCurrentVariable = new Set();
Object.keys(ruleIndexMatchedTokensCountRecord).forEach(matchedProductionRulesIndex => {
const productionRuleTokens = productionRules[parseInt(matchedProductionRulesIndex, 10)].split(" ");
// Everything after min token count will be stored as production rule of new variable
productionRulesForNewVariable.push(productionRuleTokens.slice(minTokenMatches).join(" "));
});
// Add the new production variable and its rule after the current entry index so that it can be processed further in the next iteration
cfgProductionRulesEntries.splice(currentEntryNumber + 1, 0, [newProductionVariable, productionRulesForNewVariable]);
productionRules.forEach((productionRule, productionRuleIndex) => {
const productionRuleTokens = productionRule.split(" ");
// If there are matched tokens
if (ruleIndexMatchedTokensCountRecord[productionRuleIndex]) {
// Everything before min token count will be stored as production rule of current variable
const newProductionRule = productionRuleTokens.slice(0, minTokenMatches).join(" ");
productionRulesForCurrentVariable.add(`${newProductionRule} ${newProductionVariable}`);
}
else {
// Otherwise add the production rule without changing it
productionRulesForCurrentVariable.add(productionRule);
}
});
cfgProductionRulesEntries[currentEntryNumber] = [productionVariable, Array.from(productionRulesForCurrentVariable)];
}
currentEntryNumber += 1;
}
const productionRules = {};
// Loop through new entries and populate the new production rules
cfgProductionRulesEntries.forEach(([productionVariable, productionRulesForVariable]) => {
productionRules[productionVariable] = productionRulesForVariable;
});
return (0, populateCfg_1.populateCfg)({
productionRules,
startVariable: cfg.startVariable,
});
}
exports.removeNonDeterminism = removeNonDeterminism;