/*
* 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 grammar.*;
import grammar.parse.*;
import gui.environment.GrammarEnvironment;
import java.awt.event.ActionEvent;
import java.util.*;
import javax.swing.AbstractAction;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
/**
* This controller handles user actions for the parsing of a grammar.
*
* @author Thomas Finley
*/
public class LLParseDerivationController {
/**
* Instantiates a new parse derivation controller.
*
* @param grammar
* the grammar
* @param environment
* the grammar environment
* @param firstFollow
* the first-follow table
* @param parseTable
* the parse table tableview
* @param directions
* the label that displays what step the user is on
*/
public LLParseDerivationController(Grammar grammar,
GrammarEnvironment environment, FirstFollowTable firstFollow,
LLParseTablePane parseTable, JLabel directions) {
this.environment = environment;
this.firstFollow = firstFollow;
this.parseTable = parseTable;
this.directions = directions;
this.grammar = grammar;
targetFirstSets = Operations.first(grammar);
targetFollowSets = Operations.follow(grammar);
targetParseTable = LLParseTableGenerator.generate(grammar);
nextStep();
}
/**
* 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:
return true;
case FIRST_SETS:
case FOLLOW_SETS:
Map sets = step == FIRST_SETS ? targetFirstSets : targetFollowSets;
int col = step == FIRST_SETS ? 1 : 2;
FirstFollowModel ffm = firstFollow.getFFModel();
try {
firstFollow.getCellEditor().stopCellEditing();
} catch (NullPointerException e) {
}
int highlighted = 0;
for (int i = 0; i < ffm.getRowCount(); i++) {
String var = (String) ffm.getValueAt(i, 0);
if (!ffm.getSet(i, col).equals(sets.get(var))) {
firstFollow.highlight(i, col);
highlighted++;
}
}
if (highlighted == 0)
return true;
firstFollow.clearSelection();
try {
firstFollow.getCellEditor().stopCellEditing();
} catch (NullPointerException e) {
}
JOptionPane.showMessageDialog(parseTable,
"Highlighted sets are incorrect.", "Bad Sets",
JOptionPane.ERROR_MESSAGE);
firstFollow.dehighlight();
return false;
case PARSE_TABLE:
LLParseTable pt = parseTable.getParseTable();
try {
parseTable.getCellEditor().stopCellEditing();
} catch (NullPointerException e) {
}
String[][] diff = pt.getDifferences(targetParseTable);
if (diff.length == 0)
return true;
for (int i = 0; i < diff.length; i++) {
int row = pt.getRow(diff[i][0]);
int column = pt.getColumn(diff[i][1]);
parseTable.highlight(row, column);
}
parseTable.clearSelection();
JOptionPane.showMessageDialog(firstFollow,
"Highlighted cells are incorrect.", "Bad Parse Table",
JOptionPane.ERROR_MESSAGE);
parseTable.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:
Map sets = step == FIRST_SETS ? targetFirstSets : targetFollowSets;
int col = step == FIRST_SETS ? 1 : 2;
FirstFollowModel ffm = firstFollow.getFFModel();
// Get each variable.
for (int i = 0; i < ffm.getRowCount(); i++) {
String var = (String) ffm.getValueAt(i, 0);
ffm.setSet((Set) sets.get(var), i, col);
}
try {
firstFollow.getCellEditor().stopCellEditing();
} catch (NullPointerException e) {
}
firstFollow.repaint();
nextStep();
break;
case PARSE_TABLE:
LLParseTable pt = parseTable.getParseTable();
for (int r = 0; r < pt.getRowCount(); r++) {
String var = (String) pt.getValueAt(r, 0);
for (int c = 1; c < pt.getColumnCount(); c++) {
int cv = parseTable.convertColumnIndexToView(c);
pt.setValueAt(targetParseTable.getValueAt(r, c), r, c);
}
}
try {
parseTable.getCellEditor().stopCellEditing();
} catch (NullPointerException e) {
}
parseTable.repaint();
nextStep();
break;
case FINISHED:
JOptionPane.showMessageDialog(firstFollow,
"The parse table is complete.", "Finished",
JOptionPane.ERROR_MESSAGE);
}
}
/**
* 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:
Map sets = step == FIRST_SETS ? targetFirstSets : targetFollowSets;
int col = step == FIRST_SETS ? 1 : 2;
FirstFollowModel ffm = firstFollow.getFFModel();
int viewCol = firstFollow.convertColumnIndexToView(col);
for (int i = 0; i < ffm.getColumnCount(); i++) {
if (!firstFollow.isCellSelected(i, viewCol))
continue;
// ////System.out.println("Doing row "+i);
String var = (String) ffm.getValueAt(i, 0);
ffm.setSet((Set) sets.get(var), i, col);
}
try {
firstFollow.getCellEditor().stopCellEditing();
} catch (NullPointerException e) {
}
firstFollow.repaint();
break;
case PARSE_TABLE:
LLParseTable pt = parseTable.getParseTable();
for (int r = 0; r < pt.getRowCount(); r++) {
String var = (String) pt.getValueAt(r, 0);
for (int c = 1; c < pt.getColumnCount(); c++) {
int cv = parseTable.convertColumnIndexToView(c);
if (!parseTable.isCellSelected(r, cv))
continue;
pt.setValueAt(targetParseTable.getValueAt(r, c), r, c);
}
}
// done!
try {
parseTable.getCellEditor().stopCellEditing();
} catch (NullPointerException e) {
}
parseTable.repaint();
break;
case FINISHED:
JOptionPane.showMessageDialog(firstFollow,
"The parse table is complete.", "Finished",
JOptionPane.ERROR_MESSAGE);
}
}
/**
* This finishes absolutely everything.
*/
public void completeAll() {
do {
completeStep();
} while (step != FINISHED);
}
/**
* This will handle parsing.
*/
public void parse() {
LLParsePane panel = new LLParsePane(environment, grammar,
targetParseTable);
environment.add(panel, "LL(1) Parsing");
environment.setActive(panel);
}
/**
* Checks the grammar for LL1ness.
*
* @return if the grammar is LL1
*/
public boolean isLL1() {
return Operations.isLL1(grammar);
}
/**
* 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 PARSE_TABLE:
firstFollow.getFFModel().setCanEditFollow(false);
directions
.setText("Fill entries in parse table. Use ! for a lambda entry.");
break;
case FINISHED:
try {
parseTable.getCellEditor().stopCellEditing();
} catch (NullPointerException e) {
}
doSelectedAction.setEnabled(false);
doStepAction.setEnabled(false);
doAllAction.setEnabled(false);
nextAction.setEnabled(false);
if (isLL1()) {
parseAction.setEnabled(true);
directions
.setText("Parse table complete. Press \"parse\" to use it.");
} else {
directions.setText("Parse table complete, but has ambiguity.");
}
break;
}
return true;
}
/** The grammar environment */
GrammarEnvironment environment;
/** The first-follow table */
FirstFollowTable firstFollow;
/** The parse table tableview */
private LLParseTablePane parseTable;
/** The direction label that displays the step the user is on now. */
JLabel directions;
/** Which step are we currently on? */
int step = -1;
/** The identifiers for steps. */
static final int FIRST_SETS = 0, FOLLOW_SETS = 1, PARSE_TABLE = 2,
FINISHED = 3;
/** The target first sets. */
Map targetFirstSets;
/** The target follow sets. */
Map targetFollowSets;
/** The grammar. */
Grammar grammar;
/** The target parse table. */
private LLParseTable targetParseTable;
/**
* These are actions the derivation pane uses. They are activated and
* deactivated as is appropriate.
*/
AbstractAction doSelectedAction = new AbstractAction("Do Selected") {
public void actionPerformed(ActionEvent e) {
completeSelected();
}
}, doStepAction = new AbstractAction("Do Step") {
public void actionPerformed(ActionEvent e) {
completeStep();
}
}, doAllAction = new AbstractAction("Do All") {
public void actionPerformed(ActionEvent e) {
completeAll();
}
}, nextAction = new AbstractAction("Next") {
public void actionPerformed(ActionEvent e) {
nextStep();
}
}, parseAction = new AbstractAction("Parse") {
public void actionPerformed(ActionEvent e) {
parse();
}
};
}