First and follow set solver in Javascript. This library can be used to help you write your Parsers. It is currently being used to auto generate a parser based on the rule set.
To see this library in action check out this cool tool
-
Using npm
npm install first-follow-solverfollowed byimport { solve } from 'first-follow-solver' -
Or you can compile the library by running
npm run-script buildand then adding the dist/first-follow-solver.js to your html directly:<script src="./dist/first-follow-solver.js"></script>
let problem = {
rules: [
{lhs: "S", rhs: ["a", "b"]}, // S -> a b
{lhs: "S", rhs: ["b", "b"]}, // S -> b b
{lhs: "S", rhs: ["b"]} // S -> b
],
// if not specified all symbols
// not used on the left side will be assumed to be terminals
terminals: ["a", "b"],
start: "S", // S is a default value
epsilon: "epsilon", // epsilon is the default value
eof: "$", // $ is the default value
}If included from the first-follow-solver.js file:
console.log("Solution", FirstFollowSolver.solve(problem));
If included using npm:
console.log("Solution", solve(problem));
solve(config)
validateSymbols(rules, terminals, epsilon)
extractTerminals(rules, mode, epsilon)
parseProgram(text, split)configthe configuration objectconfig.rulesan array of rulesconfig.rules.0.lhsan object representing the head of the rule (left hand side)config.rules.0.rhsan array of strings representing the right hand side symbols
config.terminalsan array of string in which every string is a terminalconfig.startstring that represents the non terminal which starts the programconfig.epsilonstring that represents the epsilon symbolconfig.eofstring that represents the end of file symbol (used for follow sets)
let problem = {
rules: [
{lhs: "S", rhs: ["a", "b"]}, // S -> a b
{lhs: "S", rhs: ["b", "b"]}, // S -> b b
{lhs: "S", rhs: ["b"]} // S -> b
],
// if not specified all symbols
// not used on the left side will be assumed to be terminals
terminals: ["a", "b"],
start: "S", // S is a default value
epsilon: "epsilon", // epsilon is the default value
eof: "$", // $ is the default value
}
console.log("Solution", FirstFollowSolver.solve(problem));
// result is {"firstSet":{"S":["a","b"]},"followSet":{"S":["$"]}}rulesan array of rulesrules.0.lhsan object representing the head of the rule (left hand side)rules.0.rhsan array of strings representing the right hand side symbols
terminalsan array of strings, each string is a terminalepsilonstring that represents the epsilon symbol
- Throws an exception that is a string containing the error if either one of the rules is broken
terminals cannot be on the left hand side
non terminals must be on the left hand side
let rules = [
{lhs: "S", rhs: ["a", "b"]}, // S -> a b
{lhs: "S", rhs: ["b", "b"]}, // S -> b b
{lhs: "S", rhs: ["b"]} // S -> b
];
let terminals = ["a", "b"];
let epsilon = "epsilon";
FirstFollowSolver.validateSymbols(rules, terminals, epsilon);rulesan array of rulesrules.0.lhsan object representing the head of the rule (left hand side)rules.0.rhsan array of strings representing the right hand side symbols
modea string that represents how the values are to be extracted- "lowercase" symbols are extracted as terminals if they contain a lowercase char
- "notLhs" symbols are extracted as terminals if they are not rule heads (left hand side)
epsilonstring that represents the epsilon symbol
let rules = [
{lhs: "S", rhs: ["a", "b"]}, // S -> a b
{lhs: "S", rhs: ["b", "b"]}, // S -> b b
{lhs: "S", rhs: ["b"]} // S -> b
];
let mode = "lowercase";
let epsilon = "epsilon";
console.log("Terminals", FirstFollowSolver.extractTerminals(rules, mode, epsilon));
// result is [a, b]textthe full program defined as a string, rules are separated into new linessplitthe string used to separate mutliple single line rules. ExampleS -> A | B
let grammarAsStr = "S -> demo | epsilon";
let split = "|";
console.log("Rules", FirstFollowSolver.parseProgram(grammarAsStr, split));
// result is [{lhs: "S", rhs: ["demo", "epsilon"]}]