UNPKG

@fauton/cfg

Version:

A package to work with context free grammars and LL1 parsers

66 lines (65 loc) 3.75 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.removeNonTerminableProduction = void 0; const isAllTerminal_1 = require("./utils/isAllTerminal"); const populateCfg_1 = require("./utils/populateCfg"); const removeProductionRules_1 = require("./utils/removeProductionRules"); const setOperations_1 = require("./utils/setOperations"); /* eslint-disable no-loop-func */ /** * Removes production rules which doesn't derive any terminals or terminable variables * @param cfg terminals, variables and production rules of cfg * @returns An array of variables that are all terminable */ function removeNonTerminableProduction(inputCfg) { const cfg = (0, populateCfg_1.populateCfg)(inputCfg); const { terminals, variables, productionRules } = cfg; // A set to keep track of variables which are terminable, ie we can reach a terminal from these variables // Initialize it with variables that derives only terminals in any of its production rules let terminableVariables = new Set(variables.filter((variable) => { var _a; // Guarding against variables that have no production rules return (_a = productionRules[variable]) === null || _a === void 0 ? void 0 : _a.some((productionRule) => (0, isAllTerminal_1.isAllTerminal)(terminals, productionRule)); })); const variablesSet = new Set(variables); let done = false; // Check if any of the rule is contains only terminable variables function checkAnyRuleIsTerminable(nonTerminableVariable) { var _a; // Guarding against variables that have no production rules return (_a = productionRules[nonTerminableVariable]) === null || _a === void 0 ? void 0 : _a.some((productionRuleSubstitution) => { // Extracting variables from substitutions const variablesFromWord = productionRuleSubstitution .split(' ') .filter((chunk) => variablesSet.has(chunk)); // Checking if all the extracted variables are terminable return variablesFromWord.every((variable) => terminableVariables.has(variable)); }); } while (!done) { done = true; // Creating a set of temporary terminal variables set to replace later const tempTerminableVariables = new Set(Array.from(terminableVariables)); // Current non terminable variables const nonTerminableVariables = (0, setOperations_1.setDifference)(variablesSet, terminableVariables); nonTerminableVariables.forEach((nonTerminableVariable) => { const isAnyVariableTerminable = checkAnyRuleIsTerminable(nonTerminableVariable); // The variable is terminable if the word contains only terminable variables if (isAnyVariableTerminable) { // Set the done flag to false, as we need to check other variables which references this one to check whether they are terminal or not done = false; tempTerminableVariables.add(nonTerminableVariable); } }); // Update the current terminable variables set with the temp one terminableVariables = tempTerminableVariables; } terminableVariables = new Set((0, removeProductionRules_1.removeProductionRules)({ productionRules, variables, removedVariables: Array.from((0, setOperations_1.setDifference)(variablesSet, terminableVariables)), })); if (!terminableVariables.has(inputCfg.startVariable)) { throw new Error(`This grammar can't be convert to cnf`); } return Array.from(terminableVariables); } exports.removeNonTerminableProduction = removeNonTerminableProduction;