/*
* Datei: Grammatik.java
* Autor(en): Lukas K�nig
* Java-Version: 1.4
* Erstellt (vor): 08.06.2007
*
* (c) Lukas K�nig, die Datei unterliegt der LGPL
* -> http://www.gnu.de/lgpl-ger.html
*/
package fmg.fmg8.earleyErkenner;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import fmg.fmg8.sonstiges.SonstMeth;
/**
* Implementiert eine Datenstruktur f�r eine kontextfreie Grammatik in der
* Form, wie sie der Earley-Parser ben�tigt (mit "Punkt"). Der Punkt befindet
* sich immer am Anfang der Regeln. Die Grammatik sollte w�hrend
* des Parsens nicht ver�ndert werden.
* Durch Aufruf der Methode <code>init()</code> kann die Grammatik wieder auf
* den urspr�nglichen Zustand gesetzt werden.
*
* @author Lukas K�nig
*/
public class EarleyGrammatik implements Serializable {
/**
* Generiert am 26.07.2007.
*/
private static final long serialVersionUID = -8810080615113388683L;
/**
* Grammatikregeln vom Typ <code>EarleyRegel</code> der Grammatik.
*/
private ArrayList<EarleyRegel> regeln;
/**
* Die Terminalsymbole der Grammatik.
*/
private ArrayList<String> terminale;
/**
* Die Nichtterminalsymbole der Grammatik.
*/
private ArrayList<String> nichtTerminale;
/**
* Konstruktor, der eine Grammatik aus einer Datei einliest und alle Felder
* initialisiert.
*
* @param dateiname Der Name der Grammatikdatei.
*/
public EarleyGrammatik(final String dateiname) {
this.liesGrammatik(dateiname);
this.extrahiereSymbole();
}
/**
* Tr�gt die zu den Grammatikregeln geh�renden Terminal- und
* Nichtterminalsymbole in die Felder <code>terminale</code>
* und <code>nichtterminale</code> ein. Dabei muss bei allen Regeln der
* Punkt ganz links stehen!
* <P>
* Nichtterminalsymbole sind alle Symbole, die mindestens einmal
* auf der linken Seite einer Regel stehen. Terminalsymbole sind alle
* �brigen Symbole. Als Startsymbol wird das Symbol definiert, das als
* erstes in der Struktur steht, also
* <code>this.nichtTerminale.get(0)</code>.
* Dies entspricht auch dem ersten Symbol in der Grammatikdatei.
*/
private void extrahiereSymbole() {
Iterator<EarleyRegel> it;
Iterator<String> it2;
EarleyRegel aktR;
String aktSymb;
this.terminale = new ArrayList<String>();
this.nichtTerminale = new ArrayList<String>();
it = this.regeln.iterator();
while (it.hasNext()) {
aktR = it.next();
if (!this.nichtTerminale.contains(aktR.getKopf())) {
this.nichtTerminale.add(aktR.getKopf());
}
}
it = this.regeln.iterator();
while (it.hasNext()) {
aktR = it.next();
it2 = aktR.getNPunkt().iterator();
while (it2.hasNext()) {
aktSymb = it2.next();
if (!this.nichtTerminale.contains(aktSymb)
&& !this.terminale.contains(aktSymb)) {
this.terminale.add(aktSymb);
}
}
}
}
/**
* Liest eine Grammatik aus der angegebenen Textdatei und erzeugt eine
* entsprechende Struktur in <code>this</code>. Dabei wird das Feld
* <code>this.regeln</code> mit den Regeln aus der Grammatikdatei
* initialisiert, nicht jedoch die Felder <code>this.terminale</code>
* und <code>this.nichtTerminale</code>. Letztere m�ssen durch
* nachtr�glichen Aufruf von <code>this.extrahiereSymbole</code>
* initialisiert werden.
*
* @param name Der Name der Grammatikdatei.
*/
private void liesGrammatik(final String name) {
Iterator<String> it;
String aktR;
ArrayList<String> stringRegeln = new ArrayList<String>();
EarleyRegel aktRegel;
ArrayList<String> rechts;
String links;
try {
RandomAccessFile file = new RandomAccessFile(name, "r");
while (file.getFilePointer() < file.length()) {
stringRegeln.add(file.readLine());
}
file.close();
} catch (final IOException e) {
System.err.println(e);
}
this.regeln = new ArrayList<EarleyRegel>();
it = stringRegeln.iterator();
while (it.hasNext()) {
aktR = it.next();
rechts = SonstMeth.zerlege(aktR, Konstanten.TRENN_GR);
links = (String) rechts.get(0);
rechts.remove(0);
aktRegel = new EarleyRegel(links, rechts);
regeln.add(aktRegel);
}
}
/**
* Textausgabe einer Grammatik.
*
* @return Die Textausgabe.
*/
public String toString() {
String s = "";
Iterator<EarleyRegel> it = this.regeln.iterator();
Iterator<String> it2;
s = s + "Regeln:\n\n";
while (it.hasNext()) {
s = s + ((EarleyRegel) it.next()).toSimpleString() + "\n";
}
s = s + "\nNichtterminale: ";
it2 = this.nichtTerminale.iterator();
while (it2.hasNext()) {
String zwisch = it2.next();
s = s + zwisch;
if (zwisch.equals(this.strSymb())) {
s = s + " (Startsymbol)";
}
if (it2.hasNext()) {
s = s + ", " + Konstanten.TRENN_EING;
}
}
s = s + "\nTerminale: ";
it2 = this.terminale.iterator();
while (it2.hasNext()) {
s = s + it2.next();
if (it2.hasNext()) {
s = s + ", " + Konstanten.TRENN_EING;
}
}
return s;
}
/**
* Gibt die Nummer des zu <code>symb</code> geh�renden Symbols zur�ck.
* Dabei gilt, dass f�r jedes Symbol aus "Nichtterminale U Terminale" genau
* eine positive Integer-Zahl zur�ckgegeben wird und f�r alle paarweise
* verschiedenen Symbole auch unterschiedliche Zahlen zur�ckgegeben werden.
* <P>
* Falls Das �bergebene Symbol nicht in der Grammatik existiert, wird -1
* zur�ckgegeben.
*
* @param symb Das Symbol, dessen Nummer berechnet werden soll.
*
* @return Die Nummer des Symbols.
*/
public int symbNum(final String symb) {
Iterator<String> it1 = this.nichtTerminale.iterator();
Iterator<String> it2 = this.terminale.iterator();
int i = 0;
while (it1.hasNext()) {
if (it1.next().equals(symb)) {
return i;
}
i++;
}
while (it2.hasNext()) {
if (it2.next().equals(symb)) {
return i;
}
i++;
}
return -1;
}
/**
* Gibt das Startsymbol zur�ck.
*
* @return Das Startsymbol.
*/
public String strSymb() {
return (String) this.nichtTerminale.get(0);
}
/**
* Ob ein Symbol ein Terminalsymbol ist.
*
* @param symb Das zu �berpr�fende Symbol.
*
* @return Ob Terminalsymbol.
*/
public boolean istTerminal(final String symb) {
Iterator<String> it = this.terminale.iterator();
while (it.hasNext()) {
if (it.next().equals(symb)) {
return true;
}
}
return false;
}
/**
* Ob ein Symbol ein Nichtterminalsymbol ist.
*
* @param symb Das zu �berpr�fende Symbol.
*
* @return Ob Nichtterminalsymbol.
*/
public boolean istNTerm(final String symb) {
return !this.istTerminal(symb);
}
/**
* @return Returns the nichtTerminale.
*/
public ArrayList<String> getNichtTerminale() {
return this.nichtTerminale;
}
/**
* @return Returns the regeln.
*/
public ArrayList<EarleyRegel> getRegeln() {
return this.regeln;
}
/**
* @return Returns the terminale.
*/
public ArrayList<String> getTerminale() {
return this.terminale;
}
/**
* Verschiebt den Punkt einer Regel um eins nach rechts.
*
* @param r Die Regel, deren Punkt verschoben werden soll.
*/
public void verschPunktR(final EarleyRegel r) {
r.verschPunktR();
}
/**
* Setzt alle Regeln (ihre Punkte) auf den Anfangszustand zur�ck.
*/
public void init() {
Iterator<EarleyRegel> it = this.regeln.iterator();
EarleyRegel aktR;
while (it.hasNext()) {
aktR = (EarleyRegel) it.next();
aktR.init();
}
}
}