UNPKG

@fauton/cfg

Version:

A package to work with context free grammars and LL1 parsers

89 lines (88 loc) 4.63 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.removeLeftRecursion = void 0; const populateCfg_1 = require("./utils/populateCfg"); function replaceProduction(pastProductionVariables, productionRules, currentVariable) { const currentVariableProductionRules = productionRules[currentVariable]; const pastProductionVariablesSet = new Set(pastProductionVariables); let currentVariableProductionRuleIndex = 0; while (currentVariableProductionRuleIndex !== currentVariableProductionRules.length) { const [firstToken, ...restTokens] = currentVariableProductionRules[currentVariableProductionRuleIndex].split(" "); if (pastProductionVariablesSet.has(firstToken)) { const matchedVariableProductionRules = productionRules[firstToken]; const newProductionRules = matchedVariableProductionRules.map(matchedVariableProductionRule => [matchedVariableProductionRule, ...restTokens].join(" ")); currentVariableProductionRules.splice(currentVariableProductionRuleIndex, 1, ...newProductionRules); } else { currentVariableProductionRuleIndex += 1; } } return currentVariableProductionRules; } function removeLeftRecursion(inputCfg) { const cfg = (0, populateCfg_1.populateCfg)(inputCfg); const productionRuleEntries = Object.entries(cfg.productionRules); let currentEntryNumber = 0; const newEntryIndexes = new Set(); while (currentEntryNumber !== productionRuleEntries.length) { const entry = productionRuleEntries[currentEntryNumber]; const [productionVariable] = entry; let productionRules = entry[1]; const betas = [], alphas = []; let index = 0; const postIndexes = []; // Checking indirect left recursion while (index !== currentEntryNumber) { if (!newEntryIndexes.has(index)) { postIndexes.push(index); } index += 1; } productionRules = postIndexes.length !== 0 ? replaceProduction(postIndexes.map(postIndex => productionRuleEntries[postIndex][0]), cfg.productionRules, productionVariable) : productionRules; // Check if atleast one of the production rule is left recursive const hasDirectLeftRecursion = productionRules.some((productionRule) => productionRule.startsWith(productionVariable)); if (hasDirectLeftRecursion) { productionRules.forEach(productionRule => { const startsWithVariable = productionRule.startsWith(productionVariable); // Aα if (startsWithVariable) { const productionRuleTokens = productionRule.split(" "); alphas.push(productionRuleTokens.slice(1).join(" ")); } // β else { betas.push(productionRule); } }); const newProductionVariable = `${productionVariable}'`; const productionRulesForNewVariable = []; alphas.forEach(alpha => { productionRulesForNewVariable.push(`${alpha} ${newProductionVariable}`); }); // Pushing epsilon productionRulesForNewVariable.push(""); // Updating current production rule const productionRulesForCurrentVariable = []; betas.forEach(beta => { productionRulesForCurrentVariable.push(`${beta} ${newProductionVariable}`); }); productionRuleEntries[currentEntryNumber] = [productionVariable, productionRulesForCurrentVariable]; productionRuleEntries.splice(currentEntryNumber + 1, 0, [newProductionVariable, productionRulesForNewVariable]); cfg.productionRules[productionVariable] = productionRulesForCurrentVariable; // Increase it by one more since we dont want to process the new entry currentEntryNumber += 1; newEntryIndexes.add(currentEntryNumber); } currentEntryNumber += 1; } const productionRules = {}; // Loop through new entries and populate the new production rules productionRuleEntries.forEach(([productionVariable, productionRulesForVariable]) => { productionRules[productionVariable] = productionRulesForVariable; }); return (0, populateCfg_1.populateCfg)({ productionRules, startVariable: cfg.startVariable, }); } exports.removeLeftRecursion = removeLeftRecursion;