UNPKG

@fauton/cfg

Version:

A package to work with context free grammars and LL1 parsers

80 lines (79 loc) 3.63 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.findFirst = void 0; const populateCfg_1 = require("./utils/populateCfg"); /** * Find first of all the variables of cfg * @param inputCfg Input context free grammar * @returns A record where keys are the variables of the cfg and the values first(variable) */ function findFirst(inputCfg) { const cfg = (0, populateCfg_1.populateCfg)(inputCfg); const { productionRules, variables } = cfg; // A record to store first(all variables) const firstRecord = {}; // Using a set for faster access const variablesSet = new Set(variables); function populateFirstRecord(productionVariable) { // Get the substitutions of that production variable const substitutions = productionRules[productionVariable]; // A set to keep store first(variable) const firstTokens = new Set(); // An array of array that stores the first for all individual rules const firstTokensSubstitutions = []; substitutions.forEach((substitution) => { // Array to contain first of this particular substitution/rule const firstTokensForSubstitution = new Set(); // a A => ["a", "A"], only the first token in required // First token could be a variable or a terminal const tokens = substitution.split(" "); const [firstToken] = tokens; // If its not a variable, then it must be a terminal if (!variablesSet.has(firstToken)) { firstTokens.add(firstToken); firstTokensForSubstitution.add(firstToken); } else { // Loop through all the tokens, as some might contain epsilon for (let index = 0; index < tokens.length; index += 1) { const token = tokens[index]; if (!variablesSet.has(token)) { firstTokensForSubstitution.add(token); firstTokens.add(token); break; } // If its a variable, // we need to check whether the findFirst(referenced variable) has been calculated or not if (!firstRecord[token]) { populateFirstRecord(token); } // Store findFirst(`referenced variable`) to current production variable record firstRecord[token].first.forEach(_token => { firstTokens.add(_token); firstTokensForSubstitution.add(_token); }); // Check to see if the first record for token contains epsilon // If it doesn't no need to check for the next tokens if (!firstRecord[token].first.includes("")) { break; } } } firstTokensSubstitutions.push(Array.from(firstTokensForSubstitution)); }); // Convert the set to an array firstRecord[productionVariable] = { first: Array.from(firstTokens), substitutions: firstTokensSubstitutions }; } // Loop through each variable variables.forEach(variable => { // Check to see if its been calculated if (!firstRecord[variable]) { populateFirstRecord(variable); } }); return firstRecord; } exports.findFirst = findFirst;