/* * 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.awt.Point; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.TreeSet; import automata.State; import automata.Transition; import automata.UnreachableStatesDetector; import automata.vdg.VDGTransition; import automata.vdg.VariableDependencyGraph; /** * As it stands now, the code in here is almost completely useless. Through * conjunction with gui.grammar.transform.UselessController it * manages to do the correct thing (I hope), but in the interest of correctness * this code should be reformed; I have too much to do right now to figure out * where the hell he was going with some of this garbage... TWF */ public class UselessProductionRemover { /** * Creates instance of UselessProductionRemover. */ public UselessProductionRemover() { } /** * Returns set of all useful variables in grammar. A grammar * is considered useful if it can derive a string. * * @param grammar * the grammar * @return set of all useful variables in grammar. A grammar * is considered useful if it can derive a string. */ public static Set getCompleteUsefulVariableSet(Grammar grammar) { Set set = getNewUsefulVariableSet(); while (areMoreVariablesThatBelongInUsefulVariableSet(grammar, set)) { String variable = getVariableThatBelongsInUsefulVariableSet( grammar, set); addToUsefulVariableSet(variable, set); } return set; } /** * Returns empty set. * * @return empty set. */ private static Set getNewUsefulVariableSet() { return new HashSet(); } /** * Adds variable to set. * * @param variable * the variable * @param set * the set */ public static void addToUsefulVariableSet(String variable, Set set) { set.add(variable); } /** * Returns the set of variables that are the predicate of rules that are * only terminal strings. */ public static Set getTerminalProductions(Grammar grammar) { Set terminalDerivers = new TreeSet(); Production[] p = grammar.getProductions(); for (int i = 0; i < p.length; i++) { String lhs = p[i].getLHS(); if (terminalDerivers.contains(lhs)) continue; String rhs = p[i].getRHS(); for (int k = 0; k < rhs.length(); k++) { char ch = rhs.charAt(k); if (ProductionChecker.isVariable(ch)) { lhs = null; break; } } if (lhs == null) continue; terminalDerivers.add(lhs); } return terminalDerivers; } /** * Get a grammar with only those variables that derive terminals, directly * or indirectly. This is not the same as a useless production-less grammar. * * @param grammar * the grammar to get the reformed grammar for */ public static Grammar getTerminalGrammar(Grammar grammar) { Grammar g = new ContextFreeGrammar(); Set terminalVars = getCompleteUsefulVariableSet(grammar); Production[] prods = grammar.getProductions(); for (int i = 0; i < prods.length; i++) { Set v = new HashSet(Arrays.asList(prods[i].getVariables())); v.removeAll(terminalVars); if (v.size() > 0) continue; // Production has no variables that aren't terminal derivers! g.addProduction(prods[i]); } g.setStartVariable(grammar.getStartVariable()); return g; } /** * Returns a variable that belongs in the set of useful variables for grammar * that is not already in set. * * @param grammar * the grammar * @param set * the set of useful variables in grammar. * @return a variable that belongs in the set of useful variables for grammar * that is not already in set. */ public static String getVariableThatBelongsInUsefulVariableSet( Grammar grammar, Set set) { String[] variables = grammar.getVariables(); for (int k = 0; k < variables.length; k++) { if (belongsInUsefulVariableSet(variables[k], grammar, set) && !set.contains(variables[k])) return variables[k]; } return null; } /** * Returns true if set contains a variable equivalent to * ch. * * @param ch * the character * @param set * the set of useful variables * @return true if set contains a variable equivalent to * ch. */ private static boolean isInUsefulVariableSet(char ch, Set set) { Iterator it = set.iterator(); while (it.hasNext()) { String variable = (String) it.next(); char var = variable.charAt(0); if (ch == var) { return true; } } return false; } /** * Returns true if production can derive a string. (i.e. if * all letters on the right hand side of the production are either terminals * or useful variables (variables in set). * * @param production * the production * @param set * the set of useful variables * @return true if production can derive a string. (i.e. if * all letters on the right hand side of the production are either * terminals or useful variables (variables in set). */ private static boolean isUsefulProduction(Production production, Set set) { ProductionChecker pc = new ProductionChecker(); String rhs = production.getRHS(); for (int k = 0; k < rhs.length(); k++) { char ch = rhs.charAt(k); if (!ProductionChecker.isTerminal(ch) && !isInUsefulVariableSet(ch, set)) { return false; } } return true; } /** * Returns true if production contains only terminals and * variables in set, the set of useful variables. This * includes both the left and right hand side of the production. * * @param production * the production * @param set * the set of useful variables * @return true if production contains only terminals and * variables in set, the set of useful variables. * This includes both the left and right hand side of the * production. */ public static boolean isValidProduction(Production production, Set set) { String lhs = production.getLHS(); for (int k = 0; k < lhs.length(); k++) { if (!isInUsefulVariableSet(lhs.charAt(k), set)) return false; } return isUsefulProduction(production, set); } /** * Returns true if variable belongs in the set of useful * variables, even if it is already in set. This function * examines all productions in grammar with variable on the * left hand side, and determines if any of those productions are useful. * * @param variable * the variable * @param grammar * the grammar * @param set * the set of useful variables * @return true if variable belongs in the set of useful * variables, even if it is already in set. */ public static boolean belongsInUsefulVariableSet(String variable, Grammar grammar, Set set) { GrammarChecker gc = new GrammarChecker(); Production[] productions = GrammarChecker.getProductionsOnVariable( variable, grammar); for (int k = 0; k < productions.length; k++) { if (isUsefulProduction(productions[k], set)) return true; } return false; } /** * Returns true if there are more variables (i.e. other than the ones * already in the set) that belong in the set of useful variables set. * * @param grammar * the grammar * @param set * the set of useful variables. * @return true if there are more variables (i.e. other than the ones * already in the set) that belong in the set of useful variables * set. */ public static boolean areMoreVariablesThatBelongInUsefulVariableSet( Grammar grammar, Set set) { if (getVariableThatBelongsInUsefulVariableSet(grammar, set) == null) return false; return true; } /** * Returns the set of all useful productions (i.e. productions that can * derive strings) in grammar based on the useful variables, * contained in usefulVariableSet. * * @param grammar * the grammar * @param usefulVariableSet * the set of useful variables * @return the set of all useful productions (i.e. productions that can * derive strings) in grammar based on the useful * variables, contained in usefulVariableSet. */ public static Set getCompleteProductionWithUsefulVariableSet( Grammar grammar, Set usefulVariableSet) { Set set = getNewProductionWithUsefulVariableSet(); Production[] productions = grammar.getProductions(); for (int k = 0; k < productions.length; k++) { if (belongsInProductionWithUsefulVariableSet(productions[k], usefulVariableSet)) { set.add(productions[k]); } } return set; } /** * Returns an empty set. * * @return an empty set. */ public static Set getNewProductionWithUsefulVariableSet() { return new HashSet(); } /** * Returns true if production belongs in set of useful * productions (i.e. if production contains only terminals * and variables in usefulVariableSet. * * @param production * the production * @param usefulVariableSet * the set of useful variables * @return true if production belongs in set of useful * productions (i.e. if production contains only * terminals and variables in usefulVariableSet. */ public static boolean belongsInProductionWithUsefulVariableSet( Production production, Set usefulVariableSet) { if (isValidProduction(production, usefulVariableSet)) { return true; } return false; } /** * Adds production to set. */ public static void addToProductionWithUsefulVariableSet( Production production, Set set) { set.add(production); } /** * Adds a state for every variable in grammar to graph, * and sets the state that represents the start variable ("S") to the * initial state. * * @param graph * the variable dependency graph * @param grammar * the grammar. */ public static void initializeVariableDependencyGraph( VariableDependencyGraph graph, Grammar grammar) { String[] variables = (String[]) getCompleteUsefulVariableSet(grammar) .toArray(new String[0]); for (int k = 0; k < variables.length; k++) { double theta = 2.0 * Math.PI * (double) k / (double) variables.length; Point point = new Point(200 + (int) (180.0 * Math.cos(theta)), 200 + (int) (180.0 * Math.sin(theta))); State state = graph.createState(point); state.setName(variables[k]); if (variables[k].equals(grammar.getStartVariable())) graph.setInitialState(state); } } /** * Returns true if v1 is dependent on v2. * (i.e. if v2 is on the right hand side of any production in * grammar that has v1 on the left hand side). * * @param v1 * the variable on the left hand side * @param v2 * the variable on the right hand side * @param grammar * the grammar * @return true if v1 is dependent on v2. * (i.e. if v2 is on the right hand side of any * production in grammar that has v1 * on the left hand side). */ public static boolean isDependentOn(String v1, String v2, Grammar grammar) { GrammarChecker gc = new GrammarChecker(); ProductionChecker pc = new ProductionChecker(); Production[] productions = GrammarChecker.getProductionsOnVariable(v1, grammar); for (int k = 0; k < productions.length; k++) { if (ProductionChecker.isVariableInProduction(v2, productions[k])) { return true; } } return false; } /** * Returns a transition between the states that represent v1 * and v2 in graph. * * @param v1 * a variable * @param v2 * a variable * @param graph * the variable dependency graph * @return a transition between the states that represent v1 * and v2 in graph. */ public static Transition getTransition(String v1, String v2, VariableDependencyGraph graph) { State from = getStateForVariable(v1, graph); State to = getStateForVariable(v2, graph); return new VDGTransition(from, to); } /** * Returns the state in graph that represents variable * (i.e the state whose name is variable). * * @param variable * the variable * @param graph * the variable dependency graph. * @return the state in graph that represents variable * (i.e the state whose name is variable). */ public static State getStateForVariable(String variable, VariableDependencyGraph graph) { State[] states = graph.getStates(); for (int k = 0; k < states.length; k++) { State state = states[k]; if (state.getName().equals(variable)) return state; } return null; } /** * Returns the variable dependency graph for grammar. * * @param grammar * the grammar * @return the variable dependency graph for grammar. */ public static VariableDependencyGraph getVariableDependencyGraph( Grammar grammar) { VariableDependencyGraph graph = new VariableDependencyGraph(); initializeVariableDependencyGraph(graph, grammar); String[] variables = (String[]) getCompleteUsefulVariableSet(grammar) .toArray(new String[0]); for (int k = 0; k < variables.length; k++) { String v1 = variables[k]; for (int i = 0; i < variables.length; i++) { String v2 = variables[i]; if (i != k && isDependentOn(v1, v2, grammar)) { Transition trans = getTransition(v1, v2, graph); graph.addTransition(trans); } } } return graph; } /** * Returns a set of transitions that represent all the dependencies * determined by production. * * @param production * the production * @param graph * the variable dependency graph * @return a set of transitions that represent all the dependencies * determined by production. */ public static Transition[] getTransitionsForProduction( Production production, VariableDependencyGraph graph) { ArrayList list = new ArrayList(); String v1 = production.getLHS(); ProductionChecker pc = new ProductionChecker(); String rhs = production.getRHS(); for (int k = 0; k < rhs.length(); k++) { char ch = rhs.charAt(k); if (ProductionChecker.isVariable(ch)) { StringBuffer buffer = new StringBuffer(); buffer.append(ch); list.add(getTransition(v1, buffer.toString(), graph)); } } return (Transition[]) list.toArray(new Transition[0]); } /** * Returns the set of variables in grammar whose productions * can never be reached from the start symbol. This is determined by the * variable dependency graph graph. * * @param grammar * the grammar * @param graph * the variable dependency graph * @return the set of variables in grammar whose productions * can never be reached from the start symbol. This is determined by * the variable dependency graph graph. */ public static String[] getUselessVariables(Grammar grammar, VariableDependencyGraph graph) { ArrayList list = new ArrayList(); UnreachableStatesDetector usd = new UnreachableStatesDetector(graph); State[] states = usd.getUnreachableStates(); for (int k = 0; k < states.length; k++) { list.add(states[k].getName()); } return (String[]) list.toArray(new String[0]); } /** * Removes all productions from grammar that contain variable, * either on the left or right hand sides. * * @param variable * the variable * @param grammar * the grammar */ public static void removeProductionsForVariable(String variable, Grammar grammar) { GrammarChecker gc = new GrammarChecker(); Production[] productions = GrammarChecker.getProductionsWithVariable( variable, grammar); for (int k = 0; k < productions.length; k++) { grammar.removeProduction(productions[k]); } } /** * Returns a grammar with no variables that can not derive strings, by * simply creating a new grammar and adding all productions in usefulProductionSet * to that grammar. * * @param usefulProductionSet * the set of useful productions * @return a grammar with no variables that can not derive strings, by * simply creating a new grammar and adding all productions in * usefulProductionSet to that grammar. */ private static Grammar getGrammarWithNoVariablesThatCantDeriveStrings( Set usefulProductionSet) { Grammar g = new ContextFreeGrammar(); Iterator it = usefulProductionSet.iterator(); while (it.hasNext()) { Production p = (Production) it.next(); g.addProduction(p); } return g; } /** * Returns a grammar, equivalent to grammar that contains no * useless productions. * * @param grammar * the grammar * @return a grammar, equivalent to grammar that contains no * useless productions. */ public static Grammar getUselessProductionlessGrammar(Grammar grammar) { Grammar g = new ContextFreeGrammar(); g.setStartVariable(grammar.getStartVariable()); if (!getCompleteUsefulVariableSet(grammar).contains( grammar.getStartVariable())) return g; grammar = getTerminalGrammar(grammar); VariableDependencyGraph graph = getVariableDependencyGraph(grammar); Set useless = new HashSet(Arrays.asList(getUselessVariables(g, graph))); Production[] p = grammar.getProductions(); for (int i = 0; i < p.length; i++) { Set variables = new HashSet(Arrays.asList(p[i].getVariables())); variables.retainAll(useless); if (variables.size() > 0) continue; g.addProduction(p[i]); } return g; } /** the start symbol. */ protected static String START_SYMBOL = "S"; }