/* * 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 automata.State; import automata.Transition; import automata.UnreachableStatesDetector; import automata.vdg.VDGTransition; import automata.vdg.VariableDependencyGraph; /** * The Unit Production remover can be used to convert a grammar to an equivalent * grammar that doesn't have any unit productions (i.e productions of the form * A->B). This conversion consists of 3 steps: 1. drawing a variable dependency * graph of all variables that have unit productions. 2. putting all non-unit * productions from the original grammar into the new gramamr. 3. adding * productions to the new grammar to replace the unit productions in the * original grammar. Each step can be performed immediately or step by step. You * can perform step 1 immediately by calling getVariableDependencyGraph, or you * can build the dependency graph step by step by first calling * initializeDependencyGraph which will add nodes for each variable in the * grammar to your dependency graph. Then, you actually represent the * dependencies of the variables by examining only the unit productions in the * grammar. To get the transition (for the graph) that represents the dependency * of a specific unit production, call getTransitionForUnitProduction. Then you * can add the returned transition to your variable dependency graph. After you * do this for every unit production in the grammar, the dependency graph will * be complete. You can perform step 2 immediately by calling * addAllNonUnitProductionsToGrammar. Or you can do this step by step by * creating a ProductionChecker object to check each production in the original * grammar to see if it is a unit production. So, if the user selects a * production from the original grammar and tries to add it to the new grammar, * you must check (using the ProductionChecker) if the production is a unit * production. If not, you can add it to the new grammar by simply calling * addProduction on the grammar. Once you've added all the Non-unit productions * from the original grammar to the new grammar, you can finish the conversion * by performing step 3. You can perform step 3 immediately by calling * addAllProductionsToGrammar, or you can perform it step by step by getting the * dependencies for each variable in the grammar by calling getDependencies. * Then, for each variable that said variable is dependent on, you need to get * all the non-unit productions in the grammar on that variable (by using a * GrammarChecker and calling getNonUnitProductionsOnVariable, and then call * getNewProductions to get the productions necessary to replace the unit * productions on said variable. This will return a list of new productions on * said variable that accounts for the removal of the unit production to its * dependent variable. * * @author Ryan Cavalcante */ public class UnitProductionRemover { /** * Creates instance of UnitProductionRemover. */ public UnitProductionRemover() { } /** * Returns the variable dependency graph for the variables in grammar. * * @param grammar * the grammar. * @return the variable dependency graph for the variables in grammar. */ public VariableDependencyGraph getVariableDependencyGraph(Grammar grammar) { VariableDependencyGraph graph = new VariableDependencyGraph(); initializeDependencyGraph(graph, grammar); Production[] uprods = getUnitProductions(grammar); for (int k = 0; k < uprods.length; k++) { graph .addTransition(getTransitionForUnitProduction(uprods[k], graph)); } return graph; } /** * Returns all unit productions in grammar. * * @param grammar * the grammar. * @return all unit productions in grammar. */ public Production[] getUnitProductions(Grammar grammar) { ArrayList list = new ArrayList(); ProductionChecker pc = new ProductionChecker(); Production[] productions = grammar.getProductions(); for (int k = 0; k < productions.length; k++) { if (ProductionChecker.isUnitProduction(productions[k])) { list.add(productions[k]); } } return (Production[]) list.toArray(new Production[0]); } /** * Returns all non-unit productions in grammar. * * @param grammar * the grammar * @return all non-unit productions in grammar. */ public Production[] getNonUnitProductions(Grammar grammar) { ArrayList list = new ArrayList(); ProductionChecker pc = new ProductionChecker(); Production[] productions = grammar.getProductions(); for (int k = 0; k < productions.length; k++) { if (!ProductionChecker.isUnitProduction(productions[k])) { list.add(productions[k]); } } return (Production[]) list.toArray(new Production[0]); } /** * Adds a state for each variable in grammar to graph. * * @param graph * the graph to add the states to. * @param grammar * the grammar */ public void initializeDependencyGraph(VariableDependencyGraph graph, Grammar grammar) { // StatePlacer sp = new StatePlacer(); String[] variables = grammar.getVariables(); for (int k = 0; k < variables.length; k++) { // Point point = sp.getPointForState(graph); 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]); } } /** * Returns the state in graph that represents variable * (i.e. the state whose label is variable). * * @param variable * the variable * @param graph * the graph * @return the state in graph that represents variable * (i.e. the state whose label is variable). */ public State getStateForVariable(String variable, VariableDependencyGraph graph) { State[] states = graph.getStates(); for (int k = 0; k < states.length; k++) { if (states[k].getName().equals(variable)) return states[k]; } return null; } /** * Returns the transition for graph that represents the * dependency of the variables in the unit production production. * * @param production * the unit production * @param graph * the graph * @return the transition for graph that represents the * dependency of the variables in the unit production production. */ public Transition getTransitionForUnitProduction(Production production, VariableDependencyGraph graph) { ProductionChecker pc = new ProductionChecker(); if (!ProductionChecker.isUnitProduction(production)) return null; String lhs = production.getLHS(); String rhs = production.getRHS(); State from = getStateForVariable(lhs, graph); State to = getStateForVariable(rhs, graph); return new VDGTransition(from, to); } /** * Adds all non-unit productions in oldGrammar to newGrammar. * * @param oldGrammar * a grammar * @param newGrammar * a grammar */ public void addAllNonUnitProductionsToGrammar(Grammar oldGrammar, Grammar newGrammar) { Production[] productions = getNonUnitProductions(oldGrammar); for (int k = 0; k < productions.length; k++) { newGrammar.addProduction(productions[k]); } } /** * Returns true if variable1 is dependent on variable2. * (i.e. there is a path in graph from variable1 * to variable2). * * @param variable1 * the first variable; the start of the path. * @param variable2 * the second variable; the destination of the path. * @param graph * the variable dependency graph. * @return true if variable1 is dependent on variable2. * (i.e. there is a path in graph from variable1 * to variable2). */ public boolean isDependentOn(String variable1, String variable2, VariableDependencyGraph graph) { State v1 = getStateForVariable(variable1, graph); State v2 = getStateForVariable(variable2, graph); graph.setInitialState(v1); UnreachableStatesDetector usd = new UnreachableStatesDetector(graph); State[] states = usd.getUnreachableStates(); graph.setInitialState(null); for (int k = 0; k < states.length; k++) { if (v2 == states[k]) return false; } return true; } /** * Returns all variables that variable is dependent on (i.e. * all variables whose states can be reached from the state that represents * variable in graph. * * @param variable * the variable whose dependencies are being found * @param grammar * the grammar * @param graph * the dependency graph * @return all variables that variable is dependent on (i.e. * all variables whose states can be reached from the state that * represents variable in graph. */ public String[] getDependencies(String variable, Grammar grammar, VariableDependencyGraph graph) { ArrayList list = new ArrayList(); String[] variables = grammar.getVariables(); for (int k = 0; k < variables.length; k++) { if (!variable.equals(variables[k])) { if (isDependentOn(variable, variables[k], graph)) { list.add(variables[k]); } } } return (String[]) list.toArray(new String[0]); } /** * Returns a list of productions created by taking variable * as their left hand side, and the right hand side of a production in * oldProductions as their right hand sides. * * @param variable * the left hand side of the created productions * @param oldProductions * the set of productions whose right hand sides are used as the * right hand sides of the created productions * @return a list of productions created by taking variable * as their left hand side, and the right hand side of a production * in oldProductions as their right hand sides. */ public Production[] getNewProductions(String variable, Production[] oldProductions) { ArrayList list = new ArrayList(); for (int k = 0; k < oldProductions.length; k++) { list.add(new Production(variable, oldProductions[k].getRHS())); } return (Production[]) list.toArray(new Production[0]); } /** * Adds all productions to newGrammar required to account for * removing all unit productions from oldGrammar. * * @param oldGrammar * the original grammar * @param newGrammar * the new grammar * @param graph * the variable dependency graph of the original grammar */ public void addAllNewProductionsToGrammar(Grammar oldGrammar, Grammar newGrammar, VariableDependencyGraph graph) { GrammarChecker gc = new GrammarChecker(); String[] variables = oldGrammar.getVariables(); for (int k = 0; k < variables.length; k++) { String v1 = variables[k]; String[] dep = getDependencies(v1, oldGrammar, graph); for (int i = 0; i < dep.length; i++) { Production[] prods = GrammarChecker .getNonUnitProductionsOnVariable(dep[i], oldGrammar); newGrammar.addProductions(getNewProductions(v1, prods)); } } } /** * Returns a unit production-less grammar equivalent to grammar. * * @param grammar * the grammar * @param graph * the variable dependency graph of grammar. * @return a unit production-less grammar equivalent to grammar. */ public Grammar getUnitProductionlessGrammar(Grammar grammar, VariableDependencyGraph graph) { Grammar uplgrammar = new ContextFreeGrammar(); addAllNonUnitProductionsToGrammar(grammar, uplgrammar); addAllNewProductionsToGrammar(grammar, uplgrammar, graph); return uplgrammar; } }