/* * 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 gui.grammar.parse; import automata.*; import automata.graph.*; import automata.graph.layout.GEMLayoutAlgorithm; import automata.fsa.FSATransition; import automata.fsa.FiniteStateAutomaton; import grammar.*; import grammar.parse.*; import gui.editor.EditorPane; import gui.environment.GrammarEnvironment; import gui.viewer.SelectionDrawer; import java.awt.Point; import java.util.*; import javax.swing.JLabel; import javax.swing.JOptionPane; /** * This controller handles user actions for the building of an LR grammar parse * table. * * @author Thomas Finley */ public class LRParseDerivationController extends LLParseDerivationController { /** * Instantiates a new parse derivation controller. * * @param grammar * the grammar * @param augmented * the augmented grammar * @param environment * the grammar environment * @param firstFollow * the first-follow table * @param directions * the label that displays what step the user is on * @param dfa * the DFA built during the derivation of the parse table * @param derivation * the view component with the user interface elements where the * derivation is performed */ public LRParseDerivationController(Grammar grammar, Grammar augmented, GrammarEnvironment environment, FirstFollowTable firstFollow, JLabel directions, FiniteStateAutomaton dfa, LRParseTableDerivationPane derivation) { super(grammar, environment, firstFollow, null, directions); this.augmented = augmented; this.dfa = dfa; this.derivation = derivation; itemChooser = new ItemSetChooser(augmented, firstFollow); } /** * For the grammar, return the initial goto item set. * * @return the initial goto item set */ private Set initialGotoSet() { Set initSet = new HashSet(); // I am one lazy son of a bitch. Production[] ps = augmented.getProductions(); Production p = ps[0]; // Oh yeah. p = new Production(p.getLHS(), GOTO_SYMBOL + p.getRHS()); initSet.add(p); initSet = Operations.closure(augmented, initSet); return initSet; } /** * Return the set of variables that have $ in their follow set. * * @return the set of variables that have $ in their follow set */ private Set variablesWithEndFollow() { Map closure = Operations.follow(grammar); Set variables = new HashSet(); Iterator it = closure.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); if (((Set) entry.getValue()).contains("$")) variables.add(entry.getKey()); } variables.add(grammar.getStartVariable() + "'"); return variables; } private boolean isFinalSet(Set set) { Iterator it = set.iterator(); while (it.hasNext()) { Production p = (Production) it.next(); if (p.getRHS().endsWith(GOTO_SYMBOL)) return true; } return false; } /** * If the current step has not been completed, this method will report back * to the user what remains to be done. * * @return true if the current step is finished, false * plus some user output if the current step is unfinished */ boolean done() { switch (step) { case -1: case FIRST_SETS: case FOLLOW_SETS: return super.done(); case BUILD_DFA: Iterator it = itemsToState.entrySet().iterator(); SelectionDrawer drawer = (SelectionDrawer) editor.getDrawer(); int selected = 0; while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); Set items = (Set) entry.getKey(); State state = (State) entry.getValue(); Transition[] t = dfa.getTransitionsFromState(state); String[] s = Operations.getCanGoto(items); if (s.length != t.length) { drawer.addSelected(state); selected++; } } if (selected != 0) { editor.repaint(); JOptionPane.showMessageDialog(firstFollow, "The indicated states need more transitions.", "Set Not Fully Expanded", JOptionPane.ERROR_MESSAGE); drawer.clearSelected(); editor.repaint(); return false; } // Now check the final states. it = itemsToState.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); Set items = (Set) entry.getKey(); State state = (State) entry.getValue(); boolean finalState = isFinalSet(items); if (finalState ^ dfa.isFinalState(state)) { drawer.addSelected(state); selected++; } } if (selected != 0) { editor.repaint(); JOptionPane .showMessageDialog( firstFollow, "The indicated states are either final and\n" + "shouldn't be, or are nonfinal and should be.", "States in Wrong Finality", JOptionPane.ERROR_MESSAGE); drawer.clearSelected(); editor.repaint(); return false; } return true; case PARSE_TABLE: int rows = targetParseTable.getRowCount(); int columns = targetParseTable.getColumnCount(); LRParseTablePane tableView = derivation.getParseTableView(); try { tableView.getCellEditor().stopCellEditing(); } catch (NullPointerException e) { } tableView.clearSelection(); int highlighted = 0; for (int i = 0; i < rows; i++) for (int j = 0; j < columns; j++) if (!targetParseTable.getValueAt(i, j).equals( userParseTable.getValueAt(i, j))) { highlighted++; tableView.highlight(i, j); } if (highlighted == 0) return true; JOptionPane.showMessageDialog(firstFollow, "Highlighted cells are incorrect.", "Bad Parse Table", JOptionPane.ERROR_MESSAGE); tableView.dehighlight(); return false; case FINISHED: JOptionPane.showMessageDialog(firstFollow, "The parse table is complete.", "Finished", JOptionPane.ERROR_MESSAGE); default: return false; } } /** * This method will complete the current step. When done with whatever it * must do it will call {@link #nextStep} to move to the next step unless it * is ont he last step, in which case a small error message is displayed. */ public void completeStep() { switch (step) { case FIRST_SETS: case FOLLOW_SETS: super.completeStep(); break; case BUILD_DFA: completeDFA(); nextStep(); break; case PARSE_TABLE: int rows = targetParseTable.getRowCount(); int columns = targetParseTable.getColumnCount(); LRParseTablePane tableView = derivation.getParseTableView(); tableView.clearSelection(); for (int i = 0; i < rows; i++) for (int j = 0; j < columns; j++) userParseTable.setValueAt( targetParseTable.getValueAt(i, j), i, j); nextStep(); break; case FINISHED: JOptionPane.showMessageDialog(firstFollow, "The parse table is complete.", "Finished", JOptionPane.ERROR_MESSAGE); break; default: System.err.println("Complete step screwed up! Step is " + step); break; } } /** * This method will complete the step for whatever cells are highlighted, as * is appropriate for the current step. */ public void completeSelected() { switch (step) { case FIRST_SETS: case FOLLOW_SETS: super.completeSelected(); break; case BUILD_DFA: JOptionPane.showMessageDialog(firstFollow, "That request is invalid for this particular step.", "Nothing Selectable", JOptionPane.ERROR_MESSAGE); break; case PARSE_TABLE: int rows = targetParseTable.getRowCount(); int columns = targetParseTable.getColumnCount(); LRParseTablePane tableView = derivation.getParseTableView(); for (int i = 0; i < rows; i++) for (int j = 0; j < columns; j++) { int cv = tableView.convertColumnIndexToView(j); if (!tableView.isCellSelected(i, cv)) continue; userParseTable.setValueAt( targetParseTable.getValueAt(i, j), i, j); } tableView.repaint(); break; } } /** * Completes the DFA. */ private void completeDFA() { if (step != BUILD_DFA) { // That shouldn't be... System.err.println("COMPLETE DFA CALLED AT WRONG TIME"); return; } // At this point, at least the initial state should exist. StatePlacer placer = new StatePlacer(); Set handledStates = new HashSet(); State[] states = dfa.getStates(); Set originalStates = new HashSet(Arrays.asList(states)); while (states.length != handledStates.size()) { for (int i = 0; i < states.length; i++) { if (handledStates.contains(states[i])) continue; Set itemSet = (Set) stateToItems.get(states[i]); if (isFinalSet(itemSet)) { dfa.addFinalState(states[i]); } else { dfa.removeFinalState(states[i]); } // See what symbols have not been "gone to" yet. Transition[] t = dfa.getTransitionsFromState(states[i]); Set mayAdd = new TreeSet(Arrays.asList(Operations .getCanGoto(itemSet))); for (int j = 0; j < t.length; j++) mayAdd.remove(((FSATransition) t[j]).getLabel()); // Now mayAdd holds symbols those we haven't done yet. Iterator it = mayAdd.iterator(); while (it.hasNext()) { String symbol = (String) it.next(); Set gotoSet = Operations.goTo(augmented, itemSet, symbol); State second = (State) itemsToState.get(gotoSet); if (second == null) { Point p = placer.getPointForState(dfa); second = dfa.createState(p); Production[] gotoArray = (Production[]) gotoSet .toArray(new Production[0]); assignItemsToState(gotoArray, second); } Transition nt = new FSATransition(states[i], second, symbol); dfa.addTransition(nt); } // That should just about do it... handledStates.add(states[i]); } states = dfa.getStates(); } LayoutAlgorithm layout = new GEMLayoutAlgorithm(); AutomatonGraph graph = new AutomatonGraph(dfa); layout.layout(graph, originalStates); graph.moveAutomatonStates(); } /** * This finishes absolutely everything. */ public void completeAll() { doAll = true; do { completeStep(); } while (step != FINISHED); doAll = false; } /** * This method is used by the embedded DFA editor to indicate that that user * wants to evaluate the Goto(I, S). I is a set of items * represented by the first parameter. S is a symbol * that the user shall input. * * @param first * the state that represents the group of items goto takes as an * argument * @param point * if a new state need be created, this will be the point that * state is created at * @param second * the state that represents the group of items goto evaluates * as; this may be null if the user has yet to * input this set */ public void gotoGroup(State first, Point point, State second) { String symbol = (String) JOptionPane.showInputDialog(firstFollow, "What is the grammar symbol for the transition?"); if (symbol == null) return; Set from = (Set) stateToItems.get(first); Set to = Operations.goTo(augmented, from, symbol); // Does this group even progress on this symbol? if (to.size() == 0) { JOptionPane.showMessageDialog(firstFollow, "That symbol is invalid for this state.", "Bad Symbol for Group", JOptionPane.ERROR_MESSAGE); return; } // Did the user drag to a state, or to empty space? if (second != null) { // We know what the second group is. Set toUser = (Set) stateToItems.get(second); if (!to.equals(toUser)) { JOptionPane.showMessageDialog(firstFollow, "The symbol " + symbol + " can't join these two states.", "Bad Progression", JOptionPane.ERROR_MESSAGE); return; } } else { // We must ask the user what the second group is. Production[] items = itemChooser .getItemSet(to, "Goto on " + symbol); if (items == null) return; Set itemSet = new HashSet(); for (int i = 0; i < items.length; i++) itemSet.add(items[i]); second = (State) itemsToState.get(itemSet); if (second == null) { second = dfa.createState(point); assignItemsToState(items, second); } } Transition t = new FSATransition(first, second, symbol); dfa.addTransition(t); } /** * Moves the controller to the next step of the building of the parse table. * * @return if the controller could be advanced to the next step */ public boolean nextStep() { if (!done()) return false; step++; switch (step) { case FIRST_SETS: parseAction.setEnabled(false); firstFollow.getFFModel().setCanEditFirst(true); firstFollow.getFFModel().setCanEditFollow(false); directions .setText("Define FIRST sets. ! is the lambda character."); break; case FOLLOW_SETS: firstFollow.getFFModel().setCanEditFirst(false); firstFollow.getFFModel().setCanEditFollow(true); directions .setText("Define FOLLOW sets. $ is the end of string character."); break; case BUILD_DFA: doSelectedAction.setEnabled(false); firstFollow.getFFModel().setCanEditFollow(false); int choice = doAll ? JOptionPane.NO_OPTION : JOptionPane .showConfirmDialog( firstFollow, "Masterfully done hero! Now you must\n" + "define the set of items for the intial DFA state.\n" + "Do you want to define the initial set yourself?", "Initial Set Construction", JOptionPane.YES_NO_OPTION); Set initialGotoSet = initialGotoSet(); Production[] initials = choice == JOptionPane.YES_OPTION ? null : (Production[]) initialGotoSet.toArray(new Production[0]); while (initials == null) { initials = itemChooser.getItemSet(initialGotoSet, "Initial Goto Set"); if (initials != null) break; JOptionPane.showMessageDialog(firstFollow, "The initial set MUST be created now.", "Initial Set Needed", JOptionPane.ERROR_MESSAGE); } State initialState = dfa.createState(new Point(60, 40)); dfa.setInitialState(initialState); assignItemsToState(initials, initialState); directions.setText("Build the DFA."); break; case PARSE_TABLE: doSelectedAction.setEnabled(true); // Set up the data structures. targetParseTable = LRParseTableGenerator.generate(augmented, dfa, stateToItems, itemsToState, Operations.follow(grammar)); userParseTable = new LRParseTable(augmented, dfa); // Set up the view. derivation.moveDFA(); derivation.setParseTable(userParseTable); directions.setText("Fill entries in parse table."); break; case FINISHED: doSelectedAction.setEnabled(false); doStepAction.setEnabled(false); doAllAction.setEnabled(false); nextAction.setEnabled(false); parseAction.setEnabled(true); // derivation.setParseTable(targetParseTable); derivation.getParseTableView().shiftMode(); directions .setText("Parse table complete. Press \"parse\" to use it."); break; } return true; } /** * This will handle parsing. */ public void parse() { LRParsePane panel = new LRParsePane(environment, augmented, userParseTable); environment.add(panel, "SLR(1) Parsing"); environment.setActive(panel); } /** * Assigns items to a particular state. * * @param items * the items to assign to a state * @param state * the state to assign the items to */ private void assignItemsToState(Production[] items, State state) { Set itemSet = new HashSet(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < items.length; i++) { itemSet.add(items[i]); if (i != 0) sb.append('\n'); sb.append(items[i].toString()); } state.setLabel(sb.toString()); stateToItems.put(state, itemSet); itemsToState.put(itemSet, state); } /** The identifiers for steps. */ static final int BUILD_DFA = 2, PARSE_TABLE = 3, FINISHED = 4; /** * The finite state automaton that displays the groups of items. */ private FiniteStateAutomaton dfa; /** The item set chooser. */ private ItemSetChooser itemChooser; /** The augmented grammar. */ private Grammar augmented; /** The parse table derivation pane. */ private LRParseTableDerivationPane derivation; /** The mapping of states to a set of items. */ private Map stateToItems = new HashMap(); /** The mapping of item sets to a state. */ private Map itemsToState = new HashMap(); /** The target parse table. */ private LRParseTable targetParseTable; /** The user defined parse table. */ private LRParseTable userParseTable; /** * This indicates that a "do all" is in progress, and as such some things * which would provide interaction should not. */ private boolean doAll = false; /* This is the goto position symbol. */ private static final String GOTO_SYMBOL = "" + Operations.ITEM_POSITION; /** The editor. */ EditorPane editor = null; }