package edu.montana.cs.fafnir.cs550.hw2;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.jdom.JDOMException;
final class Cumulus {
String title;
Alphabet alphabet;
Ruleset ruleset;
HashMap firsts;
HashMap follows;
Cumulus() {
alphabet = new Alphabet();
ruleset = new Ruleset();
}
public static void main(String[] args) {
if (args.length < 1) {
System.out.println("must have file name");
System.exit(0);
}
System.out.println();
Cumulus c = new Cumulus();
try {
c.load(new File(args[0]));
System.out.println("\"" + c.title + "\"");
System.out.println();
System.out.println();
System.out.println(c.alphabet);
System.out.println();
System.out.println(c.ruleset);
System.out.println();
c.computeFirsts();
c.displayFirsts();
System.out.println();
c.computeFollows();
c.displayFollows();
System.out.println();
} catch (FFException e) {
System.out.println(e.getMessage());
e.printStackTrace();
System.exit(-1);
}
}
void load(File source) throws FFException {
try {
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(source);
Element root = doc.getRootElement();
if (root.getName() != "GRAMMAR") {
throw new FFException("source file contained incorrect root node, \"" + root.getName() + "\"");
}
title = root.getChildText("TITLE");
for (Iterator i = root.getChildren("PRODUCTION").iterator(); i.hasNext(); ) {
Element production = (Element)i.next();
Symbol leftside = alphabet.lookupConstructively(production.getChildText("LEFTSIDE"), Alphabet.Namespace.NONTERMINAL);
ArrayList rightside = new ArrayList();
for (Iterator j = production.getChild("RIGHTSIDE").getChildren("SYMBOL").iterator(); j.hasNext(); ) {
Element symbol = (Element)j.next();
int namespace = 0;
String symbolType = symbol.getAttributeValue("Term");
if (symbolType.equals("nonterminal")) {
namespace = Alphabet.Namespace.NONTERMINAL;
} else if (symbolType.equals("terminal")) {
namespace = Alphabet.Namespace.TERMINAL;
} else {
throw new FFException("Symbol in rule was of unrecognized type \"" + symbolType + "\"");
}
String symbolName = symbol.getText();
if (symbolName.equals("lambda")) {
namespace = Alphabet.Namespace.LAMBDA;
}
rightside.add(alphabet.lookupConstructively(symbolName, namespace));
}
ruleset.add(new Rule((Nonterminal)leftside, rightside));
}
alphabet.makeStart((Nonterminal)alphabet.lookupConstructively(root.getChildText("START"), Alphabet.Namespace.NONTERMINAL));
} catch (JDOMException e) {
throw new FFException(e.getMessage());
} catch (IOException e) {
throw new FFException(e.getMessage());
}
}
private void computeFirsts() throws FFException {
firsts = new HashMap();
for (Iterator i = alphabet.getIterator(Alphabet.Namespace.NONTERMINAL); i.hasNext(); ) {
Nonterminal non = (Nonterminal)i.next();
firsts.put(non, new HashSet()); }
boolean changes = true;
while (changes) {
changes = false;
for (Iterator i = ruleset.getIterator(); i.hasNext(); ) {
Rule rule = (Rule)i.next();
Nonterminal left = rule.getLeftSide();
HashSet leftFirst = (HashSet)firsts.get(left);
for (int k = 0; k < rule.rightSideSize(); ++k) {
HashSet toAdd;
Symbol right = (Symbol)rule.getRightSide(k);
if (right instanceof Nonterminal) {
toAdd = new HashSet((HashSet)firsts.get(right));
} else {
toAdd = new HashSet(Collections.singleton(right));
}
boolean hadLambda = toAdd.remove(alphabet.lambda());
if (leftFirst.addAll(toAdd))
changes = true;
if (hadLambda) {
if (k + 1 == rule.rightSideSize()) { if (leftFirst.add(alphabet.lambda()))
changes = true;
}
continue; }
break; } } } }
private void computeFollows() throws FFException {
follows = new HashMap();
for (Iterator i = alphabet.getIterator(Alphabet.Namespace.NONTERMINAL); i.hasNext(); ) {
Nonterminal non = (Nonterminal)i.next();
follows.put(non, new HashSet()); if (non == alphabet.getStart())
((HashSet)follows.get(non)).add(new Terminal("EOF")); }
boolean changes = true;
while (changes) {
changes = false;
for (Iterator i = ruleset.getIterator(); i.hasNext(); ) {
Rule rule = (Rule)i.next();
Nonterminal left = rule.getLeftSide();
for (int k = 0; k < rule.rightSideSize(); k++) {
Symbol right = (Symbol)rule.getRightSide(k);
if (right instanceof Nonterminal) {
HashSet rightFollow = (HashSet)follows.get(right);
HashSet assembledFirsts = assemble(rule, k);
boolean hadLambda = assembledFirsts.remove(alphabet.lambda());
changes = rightFollow.addAll(assembledFirsts);
if (hadLambda)
changes = rightFollow.addAll((HashSet)follows.get(left));
} }
}
}
}
private HashSet assemble(Rule rule, int index) {
HashSet result = new HashSet();
if (index + 1 == rule.rightSideSize()) {
result.add(alphabet.lambda());
} else {
for (int k = index + 1; k < rule.rightSideSize(); ++k) {
Symbol right = (Symbol)rule.getRightSide(k);
if (right instanceof Nonterminal) {
result.addAll((HashSet)firsts.get(right));
} else { result.add(right);
if (right instanceof Lambda)
continue;
}
break;
}
}
return result;
}
void displayFirsts() {
System.out.println("first:");
System.out.println();
for (Iterator i = firsts.keySet().iterator(); i.hasNext(); ) {
Nonterminal non = (Nonterminal)i.next();
System.out.print("" + non + " -- ");
for (Iterator j = ((HashSet)firsts.get(non)).iterator(); j.hasNext(); ) {
System.out.print("" + ((Symbol)j.next()) + " ");
}
System.out.println();
}
System.out.println();
}
void displayFollows() {
System.out.println("follow:");
System.out.println();
for (Iterator i = follows.keySet().iterator(); i.hasNext(); ) {
Symbol s = (Symbol)i.next();
System.out.print("" + s + " -- ");
for (Iterator j = ((HashSet)follows.get(s)).iterator(); j.hasNext(); ) {
System.out.print("" + ((Symbol)j.next()) + " ");
}
System.out.println();
}
System.out.println();
}
}