UNPKG

@fauton/cfg

Version:

A package to work with context free grammars and LL1 parsers

113 lines (112 loc) 5.01 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.findFollow = void 0; const findFirst_1 = require("./findFirst"); const generateVariableReferenceRecord_1 = __importDefault(require("./generateVariableReferenceRecord")); const populateCfg_1 = require("./utils/populateCfg"); /** * Find follow of all the variables of cfg * @param inputCfg Input context free grammar * @returns A record where keys are variables and values is follow(variable) */ function findFollow(inputCfg) { const cfg = (0, populateCfg_1.populateCfg)(inputCfg); const { productionRules, variables, startVariable } = cfg; const followRecord = { [startVariable]: new Set() }; const variablesSet = new Set(variables); // Get the reference record for cfg const variableReferenceRecord = (0, generateVariableReferenceRecord_1.default)(cfg); // Get the first record of cfg const firstRecord = (0, findFirst_1.findFirst)(cfg); function populateFollowRecord(productionVariable, trails) { // Check to see if the production variable is the starting variable const followedTokens = new Set(); // If this is the start variable add $ if (startVariable === productionVariable) { followedTokens.add("$"); } function moveNext(reference) { const { ruleNumber, tokenNumber, variable } = reference; // If we detect a cycle // S => B => A => S // Add the follow of S to S which is $ if (trails.includes(startVariable) && variable === startVariable) { followRecord[startVariable].add("$"); } // Generate the tokens of the rule const tokens = productionRules[variable][ruleNumber].split(" "); // Check if we are add edge token or not const isAtEdge = tokens.length - 1 === tokenNumber; if (!isAtEdge) { const nextToken = tokens[tokenNumber + 1]; // If the next token is not a variable if (!variablesSet.has(nextToken)) { followedTokens.add(nextToken); } else { // Get the first tokens of next token const firstTokens = firstRecord[nextToken]; // If first of it contains nullable if (firstTokens.first.includes("")) { // Move to the next token in the rule moveNext({ ruleNumber, tokenNumber: tokenNumber + 1, variable }); } // Loop through all first tokens and add them to followed tokens firstTokens.first.forEach((firstToken) => { // Follow can never contain nullable if (firstToken !== "") { followedTokens.add(firstToken); } }); } } // If the variable is referenced at edge else if (isAtEdge) { // if the variables are not the same // B -> a B (Same), B -> a C (Different) if (variable !== productionVariable) { // Populate the follow record of the variable whose rule references this variable if (!followRecord[variable]) populateFollowRecord(variable, trails); // C -> a B // follow(B) => follow(C) followRecord[variable].forEach(token => { // Follow can never contain nullable if (token !== "") { followedTokens.add(token); } }); } } } // All the locations where this variable is being referenced const references = variableReferenceRecord[productionVariable]; references.forEach(reference => { trails.push(reference.variable); moveNext(reference); }); followRecord[productionVariable] = followedTokens; } // Go through each variables variables.forEach(variable => { populateFollowRecord(variable, [variable]); }); const transformedFollowRecord = {}; Object.entries(followRecord).forEach(([variable, followedTokens]) => { transformedFollowRecord[variable] = Array.from(followedTokens); }); return { first: firstRecord, follow: transformedFollowRecord }; } exports.findFollow = findFollow;