@fauton/cfg
Version:
A package to work with context free grammars and LL1 parsers
66 lines (65 loc) • 3.75 kB
JavaScript
;
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;