/*
* JFLAP - Formal Languages and Automata Package
*
*
* Susan H. Rodger
* Computer Science Department
* Duke University
* August 27, 2009
* Copyright (c) 2002-2009
* All rights reserved.
* JFLAP is open source software. Please see the LICENSE for terms.
*
*/
package grammar;
import grammar.cfg.ContextFreeGrammar;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* The lambda production remover object can be used to convert a grammar to an
* equivalent grammar with no lambda productions. This operation consists of 3
* steps: 1. Add all variables that have lamba productions (e.g. A->lambda) to
* lambdaSet. 2. Add all variables that have productions that are reducable to
* lambda (if B->A1A2...Am and Ai are all in lambdaSet, then put B in lambdaSet)
* to lambdaSet. 3. Construct a new grammar with productions such that for each
* production in the original grammar A->x1x2x3...xm, put all productions formed
* when xj is replaced by lambda for all xj in lambdaSet. So, if there are more
* than 1 variable in a given production from the original grammar in the
* lambdaSet, you must account for all permutations (i.e. all combinations of
* which variables are replaced by lambda and which aren't). Each of these three
* steps can be performed immediately or step by step. You can perform step 1
* immediately by calling {@link #addVariablesWithLambdaProductions} after
* creating an empty lambdaSet by calling {@link #getNewLambdaSet}. Or you can
* add the variables to lambdaSet step by step while {@link
* #areMoreVariablesWithLambdaProductions} is true, calling {@link
* #getNewVariableWithLambdaProduction} and adding it to lambdaSet. If the user
* tries to add a variable to lambdaSet, you can check if it has a lambda
* production by calling {@link #isVariableWithLambdaProduction} before adding
* it to the lambdaSet. Step 2 can also be completed immediately by calling
* {@link #getCompleteLambdaSet}. Or you can do it step by step by continually
* calling getNewVariableThatBelongsInLambdaSet while
* areMoreVariablesThatBelongInLambdaSet returns true. Or if the user tries to
* add a variable to lambdaSet, you can see if it belongs by calling
* belongsInLambdaSet. Once the lambdaSet has been completely filled with all
* variables that have lambda productions and all variables that have
* productions that can reduce to lambda, we move to step 3--constructing the
* new grammar. You can perform this step immediately by calling
* getLamdaProductionlessGrammar, or you can build it step by step by calling
* getProductionsToAddForProduction for each production in the original grammar
* and adding all returned productions to the new grammar.
*
* @author Ryan Cavalcante
*/
public class LambdaProductionRemover {
/**
* Creates instance of LambdaProductionRemover
.
*/
public LambdaProductionRemover() {
}
/**
* Returns an empty hash set
*/
public HashSet getNewLambdaSet() {
return new HashSet();
}
/**
* Adds variable
to lambdaSet
.
*
* @param variable
* the string to add to set
* @param lambdaSet
* the set to add to
*/
public void addVariableToLambdaSet(String variable, Set lambdaSet) {
if (!lambdaSet.contains(variable))
lambdaSet.add(variable);
}
/**
* Returns true if there is a production in grammar
with
* variable
on the left hand side and lambda on the right
* hand side.
*
* @param variable
* the variable
* @param grammar
* the grammar.
* @return true if there is a production in grammar
with
* variable
on the left hand side and lambda on the
* right hand side.
*/
public boolean isVariableWithLambdaProduction(String variable,
Grammar grammar) {
ProductionChecker pc = new ProductionChecker();
GrammarChecker gc = new GrammarChecker();
Production[] productions = GrammarChecker.getProductionsOnVariable(
variable, grammar);
for (int k = 0; k < productions.length; k++) {
if (ProductionChecker.isLambdaProduction(productions[k]))
return true;
}
return false;
}
/**
* Returns true if there are more variables in grammar
that
* are not already in lambdaSet
but belong there because they
* have lambda productions (e.g. A->lambda)
*
* @param grammar
* the grammar
* @param lambdaSet
* the set of variables in grammar that are on the left hand side
* of productions that go to lambda.
* @return true if there are more variables in grammar
that
* are not already in lambdaSet
but belong there
* because they have lambda productions (e.g. A->lambda)
*/
public boolean areMoreVariablesWithLambdaProductions(Grammar grammar,
Set lambdaSet) {
if (getNewVariableWithLambdaProduction(grammar, lambdaSet) == null) {
return false;
}
return true;
}
/**
* Returns a new variable (i.e. one that is not already in lambdaSet
* from grammar
that has a lambda production (i.e. a
* production with the variable on the left hand side and lambda on the
* right hand side.)
*
* @param grammar
* the grammar
* @param lambdaSet
* the set of variables that have lambda productions
* @return a new variable (i.e. one that is not already in lambdaSet
* from grammar
that has a lambda production (i.e. a
* production with the variable on the left hand side and lambda on
* the right hand side.)
*/
public String getNewVariableWithLambdaProduction(Grammar grammar,
Set lambdaSet) {
String[] variables = grammar.getVariables();
for (int k = 0; k < variables.length; k++) {
if (!lambdaSet.contains(variables[k])
&& isVariableWithLambdaProduction(variables[k], grammar)) {
return variables[k];
}
}
return null;
}
/**
* Adds all variables in grammar
that have lambda transitions
* to lambdaSet
.
*
* @param grammar
* the grammar
* @param lambdaSet
* the set of variables that have lambda transitions (upon
* returning from this method it will contain all variables in
* grammar that have lambda transitions).
*/
public void addVariablesWithLambdaProductions(Grammar grammar, Set lambdaSet) {
while (areMoreVariablesWithLambdaProductions(grammar, lambdaSet)) {
String variable = getNewVariableWithLambdaProduction(grammar,
lambdaSet);
addVariableToLambdaSet(variable, lambdaSet);
}
}
/**
* Returns true if variable
is in lambdaSet
.
*
* @param variable
* the variable
* @param lambdaSet
* the set
* @return true if variable
is in lambdaSet
.
*/
public boolean isInLambdaSet(String variable, Set lambdaSet) {
return lambdaSet.contains(variable);
}
/**
* Returns true if production
can be reduced to a lambda
* production (i.e. it is a production that goes either directly to lambda,
* or to a series of variables that all have lambda productions, or that
* themselves could be reducable to lambda productions).
*
* @param production
* the production
* @param lambdaSet
* the set of all variables that have lambda productions and
* variables that have productions that are reducable to lambda
* productions.
* @return true if production
can be reduced to a lambda
* production (i.e. it is a production that goes either directly to
* lambda, or to a series of variables that all have lambda
* productions, or that themselves could be reducable to lambda
* productions).
*/
public boolean isReducableToLambdaProduction(Production production,
Set lambdaSet) {
ProductionChecker pc = new ProductionChecker();
if (ProductionChecker.areTerminalsOnRHS(production))
return false;
String[] variables = production.getVariablesOnRHS();
for (int j = 0; j < variables.length; j++) {
if (!isInLambdaSet(variables[j], lambdaSet))
return false;
}
return true;
}
/**
* Returns true if either variable
in grammar
* has a lambda production or a production that is reducable to lambda.
*
* @param variable
* the variable
* @param grammar
* the grammar
* @param lambdaSet
* the set of all variables that either have lambda productions
* or productions that are reducable to lambda.
* @return true if either variable
in grammar
* has a lambda production or a production that is reducable to
* lambda.
*/
public boolean belongsInLambdaSet(String variable, Grammar grammar,
Set lambdaSet) {
if (isVariableWithLambdaProduction(variable, grammar))
return true;
GrammarChecker gc = new GrammarChecker();
Production[] productions = GrammarChecker.getProductionsOnVariable(
variable, grammar);
for (int k = 0; k < productions.length; k++) {
if (isReducableToLambdaProduction(productions[k], lambdaSet)) {
return true;
}
}
return false;
}
/**
* Returns true if there are more variables in grammar
that
* belong in the lambda set. This includes variables that have lambda
* productions and variables that have productions that are reducable to
* lambda.
*
* @param grammar
* the grammar
* @param lambdaSet
* the set of all variables that either have lambda productions
* or productions that are reducable to lambda.
* @return true if there are more variables in grammar
that
* belong in the lambda set. This includes variables that have
* lambda productions and variables that have productions that are
* reducable to lambda.
*/
public boolean areMoreVariablesToAddToLambdaSet(Grammar grammar,
Set lambdaSet) {
if (getNewVariableThatBelongsInLambdaSet(grammar, lambdaSet) == null)
return false;
return true;
}
/**
* Returns a variable that is not already in lambdaSet
but
* belongs there (i.e. a variable that has either a lambda production or a
* production that is reducable to lambda).
*
* @param grammar
* the grammar.
* @param lambdaSet
* the set of all variables that either have lambda productions
* or productions that are reducable to lambda.
* @return a variable that is not already in lambdaSet
but
* belongs there (i.e. a variable that has either a lambda
* production or a production that is reducable to lambda).
*/
public String getNewVariableThatBelongsInLambdaSet(Grammar grammar,
Set lambdaSet) {
String[] variables = grammar.getVariables();
for (int k = 0; k < variables.length; k++) {
if (!isInLambdaSet(variables[k], lambdaSet)
&& belongsInLambdaSet(variables[k], grammar, lambdaSet))
return variables[k];
}
return null;
}
/**
* Returns the set of all variables in grammar
that either
* have lambda productions or productions that are reducable to lambda.
*
* @param grammar
* the grammar
* @return the set of all variables in grammar
that either
* have lambda productions or productions that are reducable to
* lambda.
*/
public HashSet getCompleteLambdaSet(Grammar grammar) {
HashSet lambdaSet = getNewLambdaSet();
while (areMoreVariablesToAddToLambdaSet(grammar, lambdaSet)) {
String variable = getNewVariableThatBelongsInLambdaSet(grammar,
lambdaSet);
addVariableToLambdaSet(variable, lambdaSet);
}
return lambdaSet;
}
/**
* Returns a list of productions to add to a new grammar to replace production
.
* The returned list of productions are all permutations of production
.
* Each variable on the right hand side of production
that is
* in lambdaSet
can be replaced by lambda. So, in order to
* remove the lambda productions, we need to account for all permutations of
* production where different variables go to lambda. (e.g. if the
* production is S->AB and both A and B are in lambdaSet, then this would
* return S->AB, S->A, and S->B).
*
* @param production
* the production to replace
* @param lambdaSet
* the set of all variables that either have lambda productions
* or productions that are reducable to lambda.
* @return a list of productions to add to a new grammar to replace production
.
* The returned list of productions are all permutations of production
.
*/
public Production[] getProductionsToAddForProduction(Production production,
Set lambdaSet) {
// Stupid...
/*
* ProductionChecker pc = new ProductionChecker(); String[] variables =
* production.getVariablesOnRHS(); ArrayList list = new ArrayList();
* for(int k = 0; k < variables.length; k++) {
* if(isInLambdaSet(variables[k],lambdaSet)) list.add(variables[k]); }
* String[] lambdaVar = (String[]) list.toArray(new String[0]); String[]
* combs = getCombinations(lambdaVar); ArrayList productions = new
* ArrayList(); for(int k = 0; k < combs.length; k++) { Production p =
* getProductionForCombination(production,combs[k]);
* if(!pc.isLambdaProduction(p)) productions.add(p); } return
* (Production[]) productions.toArray(new Production[0]);
*/
String[] start = new String[] { "" };
String rhs = production.getRHS();
for (int i = 0; i < rhs.length(); i++) {
String v = rhs.substring(i, i + 1);
if (lambdaSet.contains(v)) {
String s[] = new String[start.length * 2];
for (int j = 0; j < start.length; j++) {
s[j] = start[j] + v;
s[j + start.length] = start[j];
}
start = s;
} else {
for (int j = 0; j < start.length; j++)
start[j] += v;
}
}
Arrays.sort(start);
ArrayList list = new ArrayList();
String lhs = production.getLHS();
for (int i = (start[0].length() == 0) ? 1 : 0; i < start.length; i++)
list.add(new Production(lhs, start[i]));
return (Production[]) list.toArray(new Production[0]);
}
/**
* Returns all productions created by replacing each production in grammar
* based on the variables in lambdaSet
.
*
* @param grammar
* the grammar
* @param lambdaSet
* the set of all variables that either have lambda productions
* or productions that are reducable to lambda.
* @return all productions created by replacing each production in grammar
* based on the variables in lambdaSet
.
*/
public Production[] getProductionsToAddToGrammar(Grammar grammar,
Set lambdaSet) {
ArrayList list = new ArrayList();
Production[] productions = grammar.getProductions();
for (int k = 0; k < productions.length; k++) {
Production[] prods = getProductionsToAddForProduction(
productions[k], lambdaSet);
for (int j = 0; j < prods.length; j++) {
list.add(prods[j]);
}
}
return (Production[]) list.toArray(new Production[0]);
}
/**
* Returns all non lambda productions in grammar
.
*
* @param grammar
* the grammar
* @return all non lambda productions in grammar
.
*/
public Production[] getNonLambdaProductions(Grammar grammar) {
ProductionChecker pc = new ProductionChecker();
ArrayList list = new ArrayList();
Production[] productions = grammar.getProductions();
for (int k = 0; k < productions.length; k++) {
if (!ProductionChecker.isLambdaProduction(productions[k]))
list.add(productions[k]);
}
return (Production[]) list.toArray(new Production[0]);
}
/**
* Returns a grammar equivalent to grammar
that has no lambda
* productions.
*
* @param grammar
* the grammar
* @param lambdaSet
* the set of all variables that either have lambda productions
* or productions that are reducable to lambda.
*/
public Grammar getLambdaProductionlessGrammar(Grammar grammar, Set lambdaSet) {
Grammar g = new ContextFreeGrammar();
g.addProductions(getProductionsToAddToGrammar(grammar, lambdaSet));
return g;
}
/**
* Returns true if ch
is a character in comb
.
*
* @param ch
* the character
* @param comb
* the string
* @return true if ch
is a character in comb
.
*/
private boolean isNotReplacedByLambda(char ch, String comb) {
for (int k = 0; k < comb.length(); k++) {
if (ch == comb.charAt(k))
return true;
}
return false;
}
/**
* Returns the production that represents the combination comb
.
* (e.g. if comb is the string "AB", that means that the variables "A" and
* "B" in production
should not be replaced by lambda, but
* that all other variables on the right hand side of production
* should be. therefore, if production was S->aABC, this function would
* return S->aAB).
*
* @param production
* the production
* @param comb
* the set of variables that should not be replaced by lambda.
* @return the production that represents the combination comb
.
* (e.g. if comb is the string "AB", that means that the variables
* "A" and "B" in production
should not be replaced
* by lambda, but that all other variables on the right hand side of
* production
should be. therefore, if production was
* S->aABC, this function would return S->aAB).
*/
private Production getProductionForCombination(Production production,
String comb) {
ProductionChecker pc = new ProductionChecker();
String rhs = production.getRHS();
StringBuffer buffer = new StringBuffer();
for (int k = 0; k < rhs.length(); k++) {
char ch = rhs.charAt(k);
if (ProductionChecker.isTerminal(ch)
|| isNotReplacedByLambda(ch, comb))
buffer.append(ch);
}
Production p = new Production(production.getLHS(), buffer.toString());
return p;
}
/**
* Returns a string that represents the set of variables from variables
* indicated by binary
. (e.g. if variables was the set
* "A,B,C", in that order, and binary was 011, this function would return
* "BC" since there is a one at index 1 and 2).
*
* @param binary
* a binary number whose length is the same as the length of
* variables
* @param variables
* the set of variables that binary
refers to.
* @return a string that represents the set of variables from variables
* indicated by binary
.
*/
private String getRepresentation(String binary, String[] variables) {
StringBuffer buffer = new StringBuffer();
for (int k = 0; k < binary.length(); k++) {
char ch = binary.charAt(k);
if (ch == ONE_CHAR) {
buffer.append(variables[k]);
}
}
return buffer.toString();
}
/**
* Returns a binary number equivalent to binaryNum
padded
* with enough zeros to make it a string of length length
.
*
* @param binaryNum
* the number to pad with zeros
* @param length
* the length to make binaryNum
* @return a binary number equivalent to binaryNum
padded
* with enough zeros to make it a string of length length
.
*/
private String pad(String binaryNum, int length) {
int numZeros = length - binaryNum.length();
for (int k = 0; k < numZeros; k++) {
binaryNum = ZERO.concat(binaryNum);
}
return binaryNum;
}
/**
* Returns a list of binary numbers (as strings) that encode all possible
* permutations of variables
*
* @param variables
* the set of variables
* @return a list of binary numbers (as strings) that encode all possible
* permutations of variables
*/
private String[] getCombinations(String[] variables) {
ArrayList list = new ArrayList();
for (int k = 0; k < ((int) Math.pow(2, variables.length)); k++) {
String comb = pad(Integer.toBinaryString(k), variables.length);
list.add(getRepresentation(comb, variables));
}
return (String[]) list.toArray(new String[0]);
}
/** the string for zero. */
protected String ZERO = "0";
/** the char '1' . */
protected char ONE_CHAR = '1';
}