Tinyray {
Camera { [1.0, 1.0, 5.0]; [-0.13, 0.0, 0.0]; [0.0, 1.0, 0.0] }
Ambience { [1.0, 0.3, 0.7] }
Background { [0.6, 0.0, 0.0] }
Fog { [0.3, 0.5, 0.0]; 0.12 }
Sun { [1.0, 1.0, 1.0]; [1.0, 1.0, 0.5] }
Sphere { [-0.85, 0.0, 0.0]; 1.0; [0.8, 0.5, 0.0]; [0.2, 0.5, 0.2] }
Sphere { [0.85, 0.45, 0.45]; 0.6; [0.8, 0.0, 0.0]; [0.4, 0.0, 0.4]; 0.4; 30 }
}
TINYRAY LBRACE
RBRACE
TINYRAY LBRACE RBRACE
Tinyray { }
Tinyray-Grammatik TG = (N, T, P, S) mit
N = {
Tinyray, Global, Defaults, Geo, Sun, Camera,
Background, Ambience, Fog, Bounding, Triangle,
Sphere, Plane, Parameters, Vector
},
T = {
TINYRAY := 'Tinyray', CAMERA := 'Camera',
BOUNDING := 'Bounding', SPHERE := 'Sphere',
TRIANGLE := 'Triangle', PLANE := 'Plane',
AMBIENCE := 'Ambience', SUN := 'Sun',
BACKGROUND := 'Background', FOG := 'Fog',
DEFAULTS := 'Defaults',
LBRACE := '{', RBRACE := '}',
LBRACKET := '[', RBRACKET := ']',
SEP := ','|';',
REAL := ['+'|'-'] ((('0'-'9')+) | (('0'-'9')*'.'('0'-'9')+))
[('E'|'e') ['+'|'-'] ('0'-'9')+]
},
P = {
Tinyray ::= TINYRAY LBRACE (Global | Defaults | Geo | Sun)* RBRACE,
Global ::= Camera | Background | Ambience | Fog,
Defaults ::= DEFAULTS LBRACE Parameters RBRACE,
Geo ::= Bounding | Triangle | Sphere | Plane,
Sun ::= SUN LBRACE Vector SEP Vector RBRACE,
Camera ::= CAMERA LBRACE Vector SEP Vector [ SEP Vector ] RBRACE,
Background ::= BACKGROUND LBRACE Vector RBRACE,
Ambience ::= AMBIENCE LBRACE Vector RBRACE,
Fog ::= FOG LBRACE Vector SEP REAL RBRACE,
Bounding ::= BOUNDING LBRACE (Bounding | Triangle |
Sphere | Defaults)* RBRACE,
Triangle ::= TRIANGLE LBRACE Vector SEP Vector
SEP Vector [ SEP Parameters ] RBRACE,
Sphere ::= SPHERE LBRACE Vector SEP REAL [ SEP Parameters ] RBRACE,
Plane ::= PLANE LBRACE Vector SEP Vector
SEP Vector [ SEP Parameters ] RBRACE,
Parameters ::= Vector [ SEP Vector [ SEP REAL [ SEP REAL [
SEP REAL ] ] ] ],
Vector ::= LBRACKET REAL SEP REAL SEP REAL RBRACKET
} und
S = Tinyray.
First(Tinyray) = { TINYRAY }
First(Vector) = { LBRACKET }
First(Defaults) = { DEFAULTS }
First(Sun) = { SUN }
First(Camera) = { CAMERA }
First(Background) = { BACKGROUND }
First(Ambience) = { AMBIENCE }
First(Fog) = { FOG }
First(Bounding) = { BOUNDING }
First(Triangle) = { TRIANGLE }
First(Sphere) = { SPHERE }
First(Plane) = { PLANE }
First(Parameters) = First(Vector) = { LBRACKET }
First(Global)
= First(Camera) v First(Background) v First(Ambience) v First(Fog)
= { CAMERA } v { BACKGROUND } v { AMBIENCE } v { FOG }
= { CAMERA, BACKGROUND, AMBIENCE, FOG }
First(Geo)
= First(Bounding) v First(Triangle) v First(Sphere) v First(Plane)
= { BOUNDING } v { TRIANGLE } v { SPHERE } v { PLANE }
= { BOUNDING, TRIANGLE, SPHERE, PLANE }
parse()
der Syntaxbaum erzeugt werden.
Die Rückgabe von
parse()
ist der Wurzelknoten des Syntaxbaumes.
Der Rückgabetyp wird von einer Implementierung des
ParseTreeKit
bestimmt.
package tinyray.frontend;
/**
* Die Klasse ParseException ist eine Exception, welche nur von
* einem Parser während eines Parse-Vorgangs geworfen werden sollte.
*/
public class ParseException extends Exception {
/**
* Fest vorgegebene Serialisierungskonstante,
* welche hier eigentlich nichts zur Sache tut, jedoch
* aus Versionierungsgründen angegeben werden muss.
*/
public final static long serialVersionUID = 12345678L;
/**
* Initialisiert eine ParseException mit einer Nachricht und
* dem Parser, in welchen die Exception ausgelöst wird.
* @param message Die Nachricht.
* @param parser Der Parser.
*/
public ParseException(String message, Parser<?, ?> parser) {
super(message + " {" + parser + "}");
}
/**
* Initialisert eine ParseException mit einer bereits anderen
* geworfenen Instanz wie zum Beispiel eine IOException.
* @param cause Die bereits geworfene Instanz.
*/
public ParseException(Throwable cause) {
super(cause);
}
}
package tinyray.frontend;
import java.util.List;
/**
* Das Interface ParseTreeKit stellt eine "abstrakte Fabrik" (Design-Pattern)
* dar, welche vom Parser benötigt wird, um jeden einzelnen Knoten des
* Syntaxbaumes zu erzeugen. Benötigt der Parser einen Knoten für den
* Syntaxbaum, dann instantiiert er diesen Knoten nicht selbst, sondern
* lässt ihn sich von einem ParseTreeKit erzeugen. Die Idee dabei ist,
* dass ein und derselbe Parser zu einem Tinyray-Programm unter Verwendung
* unterschiedlicher ParseTreeKits auch unterschiedliche Syntaxbäume erzeugen
* kann. Es können zum Beispiel auch im Nachhinein auch weitere ParseTreeKits
* geschrieben werden, ohne dabei in den Code des Parsers eingreifen zu
* müssen.
* Derzeit existieren zwei Implementierungen des vorliegenen ParseTreeKit:
* der RaytracerKit und der LanguageKit.
* @param <N> Die Basisklasse für einen Knoten des Syntaxbaumes.
* @param <R> Die Basisklasse für die Wurzel des Syntaxbaumes.
*/
public interface ParseTreeKit<N, R extends N> {
/**
* Erzeugt die Wurzel des Syntaxbaumes, das Tinyray-Programm.
* @param arguments Knotenliste: Beliebig viele Global-, Defaults-,
* Geo- und Sun-Knoten.
* @return Das Tinyray-Programm als abstrakter Syntaxbaum.
*/
public R getTinyray(List<N> arguments);
/**
* Erzeugt einen Camera-Knoten.
* @param arguments Knotenliste: Zwei bis drei Vector-Knoten.
* @return Der Camera-Knoten.
*/
public N getCamera(List<N> arguments);
/**
* Erzeugt einen Background-Knoten.
* @param arguments Knotenliste: Ein Vector-Knoten.
* @return Der Background-Knoten.
*/
public N getBackground(List<N> arguments);
/**
* Erzeugt einen Ambience-Knoten.
* @param arguments Knotenliste: Ein Vector-Knoten.
* @return Der Ambience-Knoten.
*/
public N getAmbience(List<N> arguments);
/**
* Erzeugt einen Fog-Knoten.
* @param arguments Knotenliste: Ein Vector- und ein Real-Knoten.
* @return Der Fog-Knoten.
*/
public N getFog(List<N> arguments);
/**
* Erzeugt einen Defaults-Knoten, die Default-Parameter.
* @param arguments Knotenliste: Ein Parameters-Knoten.
* @return Der Defaults-Knoten.
*/
public N getDefaults(List<N> arguments);
/**
* Erzeugt einen Bounding-Knoten.
* @param arguments Knotenliste: Beliebig viele Bounding-, Triangle-,
* Sphere- und Defaults-Knoten.
* @return Der Bounding-Knoten.
*/
public N getBounding(List<N> arguments);
/**
* Erzeugt einen Triangle-Knoten.
* @param arguments Knotenliste: Drei Vector-Knoten, optional gefolgt von
* einem Parameters-Knoten.
* @return Der Triangle-Knoten.
*/
public N getTriangle(List<N> arguments);
/**
* Erzeugt einen Sphere-Knoten.
* @param arguments Knotenliste: Ein Vector- und ein Real-Knoten, optional
* gefolgt von einem Parameters-Knoten.
* @return Die Sphere-Knoten.
*/
public N getSphere(List<N> arguments);
/**
* Erzeugt einen Plane-Knoten.
* @param arguments Knotenliste: Drei Vector-Knoten, optional gefolgt von
* einem Parameters-Knoten.
* @return Der Plane-Knoten.
*/
public N getPlane(List<N> arguments);
/**
* Erzeugt einen Sun-Knoten (Die Sonne).
* @param arguments Knotenliste: Zwei Vector-Knoten.
* @return Der Sun-Knoten.
*/
public N getSun(List<N> arguments);
/**
* Erzeugt einen Parameters-Knoten.
* @param arguments Knotenliste: Ein Vector-Knoten und optional einen
* weiteren Vector-Knoten, optional gefolgt
* von einem Real-Knoten, optional gefolgt
* von einem Real-Knoten, optional gefolgt
* von einem Real-Knoten.
* @return Der Parameters-Knoten.
*/
public N getParameters(List<N> arguments);
/**
* Erzeugt einen Vector-Knoten.
* @param arguments Knotenliste: Drei Vector-Knoten.
* @return Der Vector-Knoten.
*/
public N getVector(List<N> arguments);
/**
* Erzeugt einen Real-Knoten.
* @param real Eine Fließkommazahl als String.
* @return Der Real-Knoten.
*/
public N getReal(String real);
}
package tinyray.frontend;
import java.io.IOException;
import java.util.ArrayList;
/**
* Die Klasse Parser erzeut gemäß der Tinyray-Grammatik mit Hilfe
* eines Scanners und einer Fabrik (ParseTreeKit) einen entsprechenden
* Syntaxbaum. Jede Produktion der Grammatik wird durch eine Methode
* realisiert und bringt einen Teilbaum des Syntaxbaumes hervor.
* Dem ParseTreeKit wird überlassen, welche Knoten er für den Syntaxbaum
* letztendlich produzieren wird.
*
* Die Produktionen der Tinyray-Grammatik:
*
* Tinyray ::= TINYRAY LBRACE (Global | Defaults | Geo | Sun)* RBRACE .
*
* Global ::= Camera | Background | Ambience | Fog .
* Defaults ::= DEFAULTS LBRACE Parameters RBRACE .
* Geo ::= Bounding | Triangle | Sphere | Plane .
* Sun ::= SUN LBRACE Vector SEP Vector RBRACE .
*
* Camera ::= CAMERA LBRACE Vector SEP Vector [ SEP Vector ] RBRACE .
* Background ::= BACKGROUND LBRACE Vector RBRACE .
* Ambience ::= AMBIENCE LBRACE Vector RBRACE .
* Fog ::= FOG LBRACE Vector SEP REAL RBRACE .
*
* Bounding ::= BOUNDING LBRACE (Bounding | Triangle |
* Sphere | Defaults)* RBRACE .
* Triangle ::= TRIANGLE LBRACE Vector SEP Vector
* SEP Vector [ SEP Parameters ] RBRACE .
* Sphere ::= SPHERE LBRACE Vector SEP REAL [ SEP Parameters ] RBRACE .
* Plane ::= PLANE LBRACE Vector SEP Vector
* SEP Vector [ SEP Parameters ] RBRACE .
*
* Parameters ::= Vector [ SEP Vector [ SEP REAL [ SEP REAL [
* SEP REAL ] ] ] ] .
* Vector ::= LBRACKET REAL SEP REAL SEP REAL RBRACKET .
*
* Kommentare (COMMENT) werden stets geskippt und
* unbekannte Tokens (UNKNOWN) lösen eine ParseException aus.
*
* @param <N> Die Basisklasse für einen Knoten des Syntaxbaumes.
* @param <R> Die Basisklasse für die Wurzel des Syntaxbaumes.
*/
public class Parser<N, R extends N> {
// Der Scanner.
private Scanner scanner;
// Der Knotenerzeuger, die Knotenerzeugung wird vom Parse-Vorgang
// getrennt, so verfügt der Entwickler über mehr Spielraum.
private ParseTreeKit<N, R> kit;
/**
* Initialisiert den Parser mit einem Scanner und einer Fabrik.
* @param scanner Der Scanner.
* @param kit Die Fabrik.
*/
public Parser(Scanner scanner, ParseTreeKit<N, R> kit) {
this.scanner = scanner;
this.kit = kit;
}
/**
* Stößt den Parse-Vorgang an und gibt einen Syntaxbaum zurück.
* Falls das zu parsende Tinyray-Programm Grammatikfehler enthalten
* sollte, so wird jedoch eine ParseException geworfen.
* @return Der Syntaxbaum.
* @throws ParseException Die ParseException bei Grammatikfehler.
*/
public R parse() throws ParseException {
// Liest das erste Token ein.
this.tryNextToken();
if (this.hasFirstOfTinyray()) {
return this.parseTinyray();
} else {
throw new ParseException("Tinyray erwartet", this);
}
}
/* Produktionen */
/**
* Diese Methode gibt die Wurzel des Syntaxbaumes zurück
* und realisiert folgende Produktion:
* Tinyray ::= TINYRAY LBRACE (Global | Defaults | Geo | Sun)* RBRACE .
* @return Die Wurzel des Syntaxbaumes.
* @throws ParseException Die ParseException bei Grammatikfehler.
*/
private R parseTinyray() throws ParseException {
ArrayList<N> arguments = new ArrayList<N>();
this.testAndNextToken(Token.Type.TINYRAY);
this.testAndNextToken(Token.Type.LBRACE);
// Global | Defaults | Geo | Sun (beliebig oft)
while (true) {
if (this.hasFirstOfGlobal()) {
arguments.add(this.parseGlobal());
} else if (this.hasFirstOfDefaults()) {
arguments.add(this.parseDefaults());
} else if (this.hasFirstOfGeo()) {
arguments.add(this.parseGeo());
} else if (this.hasFirstOfSun()) {
arguments.add(this.parseSun());
} else {
break;
}
}
this.testAndNextToken(Token.Type.RBRACE);
return this.kit.getTinyray(arguments);
}
/**
* Diese Methode gibt einen Global-Knoten des Syntaxbaumes zurück
* und realisiert folgende Produktion:
* Global ::= Camera | Background | Ambience | Fog .
* @return Der Global-Knoten.
* @throws ParseException Die ParseException bei Grammatikfehler.
*/
private N parseGlobal() throws ParseException {
if (this.hasFirstOfCamera()) {
return this.parseCamera();
} else if (this.hasFirstOfBackground()) {
return this.parseBackground();
} else if (this.hasFirstOfAmbience()) {
return this.parseAmbience();
} else if (this.hasFirstOfFog()) {
return this.parseFog();
} else {
String message = "Camera, Background, Ambience oder Fog erwartet";
throw new ParseException(message, this);
}
}
/**
* Diese Methode gibt einen Defaults-Knoten des Syntaxbaumes zurück
* und realisiert folgende Produktion:
* Defaults ::= DEFAULTS LBRACE Parameters RBRACE .
* @return Der Defaults-Knoten.
* @throws ParseException Die ParseException bei Grammatikfehler.
*/
private N parseDefaults() throws ParseException {
ArrayList<N> arguments = new ArrayList<N>();
this.testAndNextToken(Token.Type.DEFAULTS);
this.testAndNextToken(Token.Type.LBRACE);
arguments.add(this.parseParameters());
this.testAndNextToken(Token.Type.RBRACE);
return this.kit.getDefaults(arguments);
}
/**
* Diese Methode gibt einen Geo-Knoten des Syntaxbaumes zurück
* und realisiert folgende Produktion:
* Geo ::= Bounding | Triangle | Sphere | Plane .
* @return Der Geo-Knoten.
* @throws ParseException Die ParseException bei Grammatikfehler.
*/
private N parseGeo() throws ParseException {
if (this.hasFirstOfBounding()) {
return this.parseBounding();
} else if (this.hasFirstOfTriangle()) {
return this.parseTriangle();
} else if (this.hasFirstOfSphere()) {
return this.parseSphere();
} else if (this.hasFirstOfPlane()) {
return this.parsePlane();
} else {
String message = "Bounding, Triangle, Sphere oder Plane erwartet";
throw new ParseException(message, this);
}
}
/**
* Diese Methode gibt einen Sun-Knoten des Syntaxbaumes zurück
* und realisiert folgende Produktion:
* Sun ::= SUN LBRACE Vector SEP Vector RBRACE .
* @return Der Sun-Knoten.
* @throws ParseException Die ParseException bei Grammatikfehler.
*/
private N parseSun() throws ParseException {
ArrayList<N> arguments = new ArrayList<N>();
this.testAndNextToken(Token.Type.SUN);
this.testAndNextToken(Token.Type.LBRACE);
arguments.add(this.parseVector());
this.testAndNextToken(Token.Type.SEP);
arguments.add(this.parseVector());
this.testAndNextToken(Token.Type.RBRACE);
return this.kit.getSun(arguments);
}
/**
* Diese Methode gibt einen Camera-Knoten des Syntaxbaumes zurück
* und realisiert folgende Produktion:
* Camera ::= CAMERA LBRACE Vector SEP Vector [ SEP Vector ] RBRACE .
* @return Der Camera-Knoten.
* @throws ParseException Die ParseException bei Grammatikfehler.
*/
private N parseCamera() throws ParseException {
ArrayList<N> arguments = new ArrayList<N>();
this.testAndNextToken(Token.Type.CAMERA);
this.testAndNextToken(Token.Type.LBRACE);
arguments.add(this.parseVector());
this.testAndNextToken(Token.Type.SEP);
arguments.add(this.parseVector());
// 3. Vektor (optional)
if (this.getToken().getType() == Token.Type.SEP) {
this.tryNextToken();
arguments.add(this.parseVector());
}
this.testAndNextToken(Token.Type.RBRACE);
return this.kit.getCamera(arguments);
}
/**
* Diese Methode gibt einen Background-Knoten des Syntaxbaumes zurück
* und realisiert folgende Produktion:
* Background ::= BACKGROUND LBRACE Vector RBRACE .
* @return Der Background-Knoten.
* @throws ParseException Die ParseException bei Grammatikfehler.
*/
private N parseBackground() throws ParseException {
ArrayList<N> arguments = new ArrayList<N>();
this.testAndNextToken(Token.Type.BACKGROUND);
this.testAndNextToken(Token.Type.LBRACE);
arguments.add(this.parseVector());
this.testAndNextToken(Token.Type.RBRACE);
return this.kit.getBackground(arguments);
}
/**
* Diese Methode gibt einen Ambience-Knoten des Syntaxbaumes zurück
* und realisiert folgende Produktion:
* Ambience ::= AMBIENCE LBRACE Vector RBRACE .
* @return Der Ambience-Knoten.
* @throws ParseException Die ParseException bei Grammatikfehler.
*/
private N parseAmbience() throws ParseException {
ArrayList<N> arguments = new ArrayList<N>();
this.testAndNextToken(Token.Type.AMBIENCE);
this.testAndNextToken(Token.Type.LBRACE);
arguments.add(this.parseVector());
this.testAndNextToken(Token.Type.RBRACE);
return this.kit.getAmbience(arguments);
}
/**
* Diese Methode gibt einen Fog-Knoten des Syntaxbaumes zurück
* und realisiert folgende Produktion:
* Fog ::= FOG LBRACE Vector SEP REAL RBRACE .
* @return Der Fog-Knoten.
* @throws ParseException Die ParseException bei Grammatikfehler.
*/
private N parseFog() throws ParseException {
ArrayList<N> arguments = new ArrayList<N>();
this.testAndNextToken(Token.Type.FOG);
this.testAndNextToken(Token.Type.LBRACE);
arguments.add(this.parseVector());
this.testAndNextToken(Token.Type.SEP);
arguments.add(this.parseReal());
this.testAndNextToken(Token.Type.RBRACE);
return this.kit.getFog(arguments);
}
/**
* Diese Methode gibt einen Bounding-Knoten des Syntaxbaumes zurück
* und realisiert folgende Produktion:
* Bounding ::= BOUNDING LBRACE (Bounding | Triangle |
* Sphere | Defaults)* RBRACE .
* @return Der Bounding-Knoten.
* @throws ParseException Die ParseException bei Grammatikfehler.
*/
private N parseBounding() throws ParseException {
ArrayList<N> arguments = new ArrayList<N>();
this.testAndNextToken(Token.Type.BOUNDING);
this.testAndNextToken(Token.Type.LBRACE);
// Bounding | Triangle | Sphere | Defaults (beliebig oft)
while (true) {
if (this.hasFirstOfBounding()) {
arguments.add(this.parseBounding());
} else if (this.hasFirstOfTriangle()) {
arguments.add(this.parseTriangle());
} else if (this.hasFirstOfSphere()) {
arguments.add(this.parseSphere());
} else if (this.hasFirstOfDefaults()) {
arguments.add(this.parseDefaults());
} else {
break;
}
}
this.testAndNextToken(Token.Type.RBRACE);
return this.kit.getBounding(arguments);
}
/**
* Diese Methode gibt einen Triangle-Knoten des Syntaxbaumes zurück
* und realisiert folgende Produktion:
* Triangle ::= TRIANGLE LBRACE Vector SEP Vector
* SEP Vector [ SEP Parameters ] RBRACE .
* @return Der Triangle-Knoten.
* @throws ParseException Die ParseException bei Grammatikfehler.
*/
private N parseTriangle() throws ParseException {
ArrayList<N> arguments = new ArrayList<N>();
this.testAndNextToken(Token.Type.TRIANGLE);
this.testAndNextToken(Token.Type.LBRACE);
arguments.add(this.parseVector());
this.testAndNextToken(Token.Type.SEP);
arguments.add(this.parseVector());
this.testAndNextToken(Token.Type.SEP);
arguments.add(this.parseVector());
// SEP Parameters (optional)
if (this.getToken().getType() == Token.Type.SEP) {
this.tryNextToken();
arguments.add(this.parseParameters());
}
this.testAndNextToken(Token.Type.RBRACE);
return this.kit.getTriangle(arguments);
}
/**
* Diese Methode gibt einen Sphere-Knoten des Syntaxbaumes zurück
* und realisiert folgende Produktion:
* Sphere ::= SPHERE LBRACE Vector SEP REAL [ SEP Parameters ] RBRACE .
* @return Der Sphere-Knoten.
* @throws ParseException Die ParseException bei Grammatikfehler.
*/
private N parseSphere() throws ParseException {
ArrayList<N> arguments = new ArrayList<N>();
this.testAndNextToken(Token.Type.SPHERE);
this.testAndNextToken(Token.Type.LBRACE);
arguments.add(this.parseVector());
this.testAndNextToken(Token.Type.SEP);
arguments.add(this.parseReal());
// SEP Parameters (optional)
if (this.getToken().getType() == Token.Type.SEP) {
this.tryNextToken();
arguments.add(this.parseParameters());
}
this.testAndNextToken(Token.Type.RBRACE);
return this.kit.getSphere(arguments);
}
/**
* Diese Methode gibt einen Plane-Knoten des Syntaxbaumes zurück
* und realisiert folgende Produktion:
* Plane ::= PLANE LBRACE Vector SEP Vector
* SEP Vector [ SEP Parameters ] RBRACE .
* @return Der Plane-Knoten.
* @throws ParseException Die ParseException bei Grammatikfehler.
*/
private N parsePlane() throws ParseException {
ArrayList<N> arguments = new ArrayList<N>();
this.testAndNextToken(Token.Type.PLANE);
this.testAndNextToken(Token.Type.LBRACE);
arguments.add(this.parseVector());
this.testAndNextToken(Token.Type.SEP);
arguments.add(this.parseVector());
this.testAndNextToken(Token.Type.SEP);
arguments.add(this.parseVector());
// SEP Parameters (optional)
if (this.getToken().getType() == Token.Type.SEP) {
this.tryNextToken();
arguments.add(this.parseParameters());
}
this.testAndNextToken(Token.Type.RBRACE);
return this.kit.getPlane(arguments);
}
/**
* Diese Methode gibt einen Parameters-Knoten des Syntaxbaumes zurück
* und realisiert folgende Produktion:
* Parameters ::= Vector [ SEP Vector [ SEP REAL
* [ SEP REAL [ SEP REAL ] ] ] ] .
* @return Der Parameters-Knoten.
* @throws ParseException Die ParseException bei Grammatikfehler.
*/
private N parseParameters() throws ParseException {
if (this.hasFirstOfParameters()) {
ArrayList<N> arguments = new ArrayList<N>();
arguments.add(this.parseVector());
// SEP Vector [ SEP REAL [ SEP REAL [ SEP REAL ] ] ] (optional)
if (this.getToken().getType() == Token.Type.SEP) {
this.tryNextToken();
arguments.add(this.parseVector());
// SEP REAL [ SEP REAL [ SEP REAL ] ] (optional)
if (this.getToken().getType() == Token.Type.SEP) {
this.tryNextToken();
arguments.add(this.parseReal());
// SEP REAL [ SEP REAL ] (optional)
if (this.getToken().getType() == Token.Type.SEP) {
this.tryNextToken();
arguments.add(this.parseReal());
// SEP REAL (optional)
if (this.getToken().getType() == Token.Type.SEP) {
this.tryNextToken();
arguments.add(this.parseReal());
}
}
}
}
return this.kit.getParameters(arguments);
} else {
throw new ParseException("Parameters erwartet", this);
}
}
/**
* Diese Methode gibt einen Vector-Knoten des Syntaxbaumes zurück
* und realisiert folgende Produktion:
* Vector ::= LBRACKET REAL SEP REAL SEP REAL RBRACKET .
* @return Der Vector-Knoten.
* @throws ParseException Die ParseException bei Grammatikfehler.
*/
private N parseVector() throws ParseException {
if (this.hasFirstOfVector()) {
ArrayList<N> arguments = new ArrayList<N>();
this.testAndNextToken(Token.Type.LBRACKET);
arguments.add(this.parseReal());
this.testAndNextToken(Token.Type.SEP);
arguments.add(this.parseReal());
this.testAndNextToken(Token.Type.SEP);
arguments.add(this.parseReal());
this.testAndNextToken(Token.Type.RBRACKET);
return this.kit.getVector(arguments);
} else {
throw new ParseException("Vector erwartet", this);
}
}
/**
* Diese Methode gibt einen Real-Knoten des Syntaxbaumes zurück.
* @return Der Real-Knoten.
* @throws ParseException Die ParseException bei Grammatikfehler.
*/
private N parseReal() throws ParseException {
Token real = this.testAndNextToken(Token.Type.REAL);
return this.kit.getReal(real.getValue());
}
/* First-Mengen */
/**
* Prüft, ob das zuletzt gelesene Token in der Firstmenge
* der Tinyray-Produktion enthalten ist.
* @return true, das Token befindet sich in der Menge.
*/
private boolean hasFirstOfTinyray() {
return this.getToken().getType() == Token.Type.TINYRAY;
}
/**
* Prüft, ob das zuletzt gelesene Token in der Firstmenge
* der Global-Produktion enthalten ist.
* @return true, das Token befindet sich in der Menge.
*/
private boolean hasFirstOfGlobal() {
return (this.hasFirstOfCamera())
|| (this.hasFirstOfBackground())
|| (this.hasFirstOfAmbience())
|| (this.hasFirstOfFog());
}
/**
* Prüft, ob das zuletzt gelesene Token in der Firstmenge
* der Background-Produktion enthalten ist.
* @return true, das Token befindet sich in der Menge.
*/
private boolean hasFirstOfBackground() {
return this.getToken().getType() == Token.Type.BACKGROUND;
}
/**
* Prüft, ob das zuletzt gelesene Token in der Firstmenge
* der Ambience-Produktion enthalten ist.
* @return true, das Token befindet sich in der Menge.
*/
private boolean hasFirstOfAmbience() {
return this.getToken().getType() == Token.Type.AMBIENCE;
}
/**
* Prüft, ob das zuletzt gelesene Token in der Firstmenge
* der Fog-Produktion enthalten ist.
* @return true, das Token befindet sich in der Menge.
*/
private boolean hasFirstOfFog() {
return this.getToken().getType() == Token.Type.FOG;
}
/**
* Prüft, ob das zuletzt gelesene Token in der Firstmenge
* der Defaults-Produktion enthalten ist.
* @return true, das Token befindet sich in der Menge.
*/
private boolean hasFirstOfDefaults() {
return this.getToken().getType() == Token.Type.DEFAULTS;
}
/**
* Prüft, ob das zuletzt gelesene Token in der Firstmenge
* der Camera-Produktion enthalten ist.
* @return true, das Token befindet sich in der Menge.
*/
private boolean hasFirstOfCamera() {
return this.getToken().getType() == Token.Type.CAMERA;
}
/**
* Prüft, ob das zuletzt gelesene Token in der Firstmenge
* der Geo-Produktion enthalten ist.
* @return true, das Token befindet sich in der Menge.
*/
private boolean hasFirstOfGeo() {
return (this.hasFirstOfBounding())
|| (this.hasFirstOfTriangle())
|| (this.hasFirstOfSphere())
|| (this.hasFirstOfPlane());
}
/**
* Prüft, ob das zuletzt gelesene Token in der Firstmenge
* der Bounding-Produktion enthalten ist.
* @return true, das Token befindet sich in der Menge.
*/
private boolean hasFirstOfBounding() {
return this.getToken().getType() == Token.Type.BOUNDING;
}
/**
* Prüft, ob das zuletzt gelesene Token in der Firstmenge
* der Triangle-Produktion enthalten ist.
* @return true, das Token befindet sich in der Menge.
*/
private boolean hasFirstOfTriangle() {
return this.getToken().getType() == Token.Type.TRIANGLE;
}
/**
* Prüft, ob das zuletzt gelesene Token in der Firstmenge
* der Sphere-Produktion enthalten ist.
* @return true, das Token befindet sich in der Menge.
*/
private boolean hasFirstOfSphere() {
return this.getToken().getType() == Token.Type.SPHERE;
}
/**
* Prüft, ob das zuletzt gelesene Token in der Firstmenge
* der Plane-Produktion enthalten ist.
* @return true, das Token befindet sich in der Menge.
*/
private boolean hasFirstOfPlane() {
return this.getToken().getType() == Token.Type.PLANE;
}
/**
* Prüft, ob das zuletzt gelesene Token in der Firstmenge
* der Sun-Produktion enthalten ist.
* @return true, das Token befindet sich in der Menge.
*/
private boolean hasFirstOfSun() {
return this.getToken().getType() == Token.Type.SUN;
}
/**
* Prüft, ob das zuletzt gelesene Token in der Firstmenge
* der Parameters-Produktion enthalten ist.
* @return true, das Token befindet sich in der Menge.
*/
private boolean hasFirstOfParameters() {
return this.hasFirstOfVector();
}
/**
* Prüft, ob das zuletzt gelesene Token in der Firstmenge
* der Vector-Produktion enthalten ist.
* @return true, das Token befindet sich in der Menge.
*/
private boolean hasFirstOfVector() {
return this.getToken().getType() == Token.Type.LBRACKET;
}
/* Hilfsmethoden */
/**
* Gibt das zuletzt gelesene Token des Scanners zurück.
* @return Das zuletzt gelesene Token.
*/
private Token getToken() {
return this.scanner.getToken();
}
/**
* Linear-rekursive Methode, die das nächstgültige Token zurückgibt.
* Kommentare werden solange übersprungen (geskippt) bis ein gültiges
* Token anliegt. Sobald ein unbekanntes Token erkannt wird, wird eine
* ParseException geworfen. Eine IOException wird als ParseException
* geworfen (vielleicht nicht ganz korrekt).
* @throws ParseException Die ParseException.
*/
private void tryNextToken() throws ParseException {
try {
this.scanner.nextToken();
} catch (IOException ioe) {
throw new ParseException(ioe);
}
if (this.getToken().getType() == Token.Type.UNKNOWN) {
throw new ParseException("Unbekanntes Token", this);
} else if (this.getToken().getType() == Token.Type.COMMENT) {
this.tryNextToken();
}
}
/**
* Prüft, ob der aktuell anliegende Token vom gegebenen Tokentyp ist
* und veranlasst den Scanner zum nächstgültigen Token zu lesen
* (siehe tryNextToken).
* Ist das aktuell anliegende Token nicht vom gegebenen Tokentyp,
* so wird eine ParseException worfen.
* @param type Der gegebene Tokentyp.
* @return Das aktuell anliegende Token.
* @throws ParseException Die ParseException.
*/
private Token testAndNextToken(Token.Type type) throws ParseException {
if (this.getToken().getType() == type) {
Token result = this.getToken();
this.tryNextToken();
return result;
} else {
throw new ParseException(type + " erwartet", this);
}
}
/**
* Gibt die textuelle Repräsentation des Scanners zurück,
* dabei interessiert eigentlich nur das aktuell anliegende,
* also zuletzt gelesene Token.
*/
public String toString() {
return this.getToken().toString();
}
}
ParseTreeKit
(Interface).
Eine prägnante Implementierung des ParseTreeKit, welche zudem auch die Struktur des Syntaxbaumes gut hervorhebt,
heißt im Folgenden
XMLTreeKit.
Der XMLTreeKit erzeugt die Knoten des Syntaxbaumes als
String-Listen
„List<String>”,
welche jeweils
XML-Code-Fragmente
enthalten.
Das Anwendungsbeispiel heißt
XMLTree
und gibt den Syntaxbaum als XML-Baum aus.
package tinyray.examples;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import tinyray.frontend.ParseException;
import tinyray.frontend.ParseTreeKit;
import tinyray.frontend.Parser;
import tinyray.frontend.Scanner;
public class XMLTree {
public static class XMLTreeKit
implements ParseTreeKit<List<String>, List<String>> {
public List<String> getTinyray(List<List<String>> arguments) {
return tag("Tinyray", arguments);
}
public List<String> getCamera(List<List<String>> arguments) {
return tag("Camera", arguments);
}
public List<String> getBackground(List<List<String>> arguments) {
return tag("Background", arguments);
}
public List<String> getAmbience(List<List<String>> arguments) {
return tag("Ambience", arguments);
}
public List<String> getFog(List<List<String>> arguments) {
return tag("Fog", arguments);
}
public List<String> getDefaults(List<List<String>> arguments) {
return tag("Defaults", arguments);
}
public List<String> getBounding(List<List<String>> arguments) {
return tag("Bounding", arguments);
}
public List<String> getTriangle(List<List<String>> arguments) {
return tag("Triangle", arguments);
}
public List<String> getSphere(List<List<String>> arguments) {
return tag("Sphere", arguments);
}
public List<String> getPlane(List<List<String>> arguments) {
return tag("Plane", arguments);
}
public List<String> getSun(List<List<String>> arguments) {
return tag("Sun", arguments);
}
public List<String> getParameters(List<List<String>> arguments) {
return tag("Parameters", arguments);
}
public List<String> getVector(List<List<String>> arguments) {
return tag("Vector", arguments);
}
public List<String> getReal(String real) {
ArrayList<String> result = new ArrayList<String>();
result.add("<Real>" + Double.parseDouble(real) + "</Real>");
return result;
}
private List<String> tag(String name, List<List<String>> arguments) {
ArrayList<String> result = new ArrayList<String>();
result.add("<" + name + ">");
for (int i = 0; i < arguments.size(); i++) {
for (int j = 0; j < arguments.get(i).size(); j++) {
result.add("\t" + arguments.get(i).get(j));
}
}
result.add("</" + name + ">");
return result;
}
}
public static void main(String[] arguments) {
if (arguments.length > 0) {
try {
// 1. Der Scanner erhält den Tinyray-Code als Stream.
Scanner scanner =
new Scanner(new FileInputStream(arguments[0]));
// 2. Den ParseTreeKit erzeugen.
XMLTreeKit kit = new XMLTreeKit();
// 3. Den Parser mit Scanner- und Kit-Instanz erzeugen.
Parser<List<String>, List<String>> parser =
new Parser<List<String>, List<String>>(scanner, kit);
// 4. Parsen!
List<String> tinyray = parser.parse();
// 5. Die Ausgabe des Syntaxbaumes (hier: XML-Baum).
System.out.print("<?xml version=\"1.0\"");
System.out.println(" encoding=\"ISO-8859-1\"?>");
for (int i = 0; i < tinyray.size(); i++) {
System.out.println(tinyray.get(i));
}
} catch (ParseException e) {
System.err.println(e.getMessage());
} catch (FileNotFoundException e) {
System.err.println(e.getMessage());
}
} else {
System.err.println("Tinyray-Programm-Datei angeben.");
}
}
}
// Hallo Welt!
Tinyray {
Sphere {
[0, 0, 0]; // Wo liegt die Kugel?
2.0 // Welchen Radius besitzt die Kugel?
}
Sun {
[1, 1, 1]; // In welcher Richtung befindet sich die Sonne?
[1, 1, 1] // Welche Farbe hat die Sonne?
}
}
<?xml version="1.0" encoding="ISO-8859-1"?>
<Tinyray>
<Sphere>
<Vector>
<Real>0.0</Real>
<Real>0.0</Real>
<Real>0.0</Real>
</Vector>
<Real>2.0</Real>
</Sphere>
<Sun>
<Vector>
<Real>1.0</Real>
<Real>1.0</Real>
<Real>1.0</Real>
</Vector>
<Vector>
<Real>1.0</Real>
<Real>1.0</Real>
<Real>1.0</Real>
</Vector>
</Sun>
</Tinyray>
Tinyray {
Camera { [0.0, 0.0, 9.0]; [1.6, 1.1, 0.0]; [0.0, 1.0, 0.0] }
Ambience { [0.4, 0.6, 0.0] }
Background { [0.0, 0.4, 0.7] }
Fog { [0.3, 0.3, 0.8]; 0.065 }
Sun { [1.0, 1.0, 1.0]; [1.0, 1.0, 1.0] }
Sun { [-0.866, -0.5, 0.05]; [0.8, 0.8, 0.4] }
Sun { [-1.0, 0.2, 1.0]; [0.5, 1.0, 1.0] }
Defaults { [0.4, 0.4, 0.4]; [0.0, 0.0, 0.0]; 0.2; 10.0; 0.6 }
Plane { [4.0, 0.0, 0.0]; [0.0, 4.0, 0.0]; [0.0, 0.0, -2.0] }
Defaults { [0.7, 0.0, 0.0]; [0.0, 0.1, 0.0]; 1.2; 9.0 }
Triangle { [0.0, 0.0, 1.5]; [0.0, 1.0, 0.0]; [-0.866, -0.5, 0.0] }
Defaults { [0.7, 0.0, 0.0]; [0.3, 0.1, 0.0] }
Triangle { [0.0, 0.0, 1.5]; [-0.866, -0.5, 0.0]; [0.866, -0.5, 0.0] }
Defaults { [0.7, 0.0, 0.0]; [0.6, 0.1, 0.0] }
Triangle { [0.0, 0.0, 1.5]; [0.866, -0.5, 0.0]; [0.0, 1.0, 0.0] }
Defaults { [0.7, 0.0, 0.0]; [0.9, 0.1, 0.0] }
Triangle { [0.0, 1.0, 0.0]; [-0.866, -0.5, 0.0]; [0.866, -0.5, 0.0] }
}
<?xml version="1.0" encoding="ISO-8859-1"?>
<Tinyray>
<Camera>
<Vector>
<Real>0.0</Real>
<Real>0.0</Real>
<Real>9.0</Real>
</Vector>
<Vector>
<Real>1.6</Real>
<Real>1.1</Real>
<Real>0.0</Real>
</Vector>
<Vector>
<Real>0.0</Real>
<Real>1.0</Real>
<Real>0.0</Real>
</Vector>
</Camera>
<Ambience>
<Vector>
<Real>0.4</Real>
<Real>0.6</Real>
<Real>0.0</Real>
</Vector>
</Ambience>
<Background>
<Vector>
<Real>0.0</Real>
<Real>0.4</Real>
<Real>0.7</Real>
</Vector>
</Background>
<Fog>
<Vector>
<Real>0.3</Real>
<Real>0.3</Real>
<Real>0.8</Real>
</Vector>
<Real>0.065</Real>
</Fog>
<Sun>
<Vector>
<Real>1.0</Real>
<Real>1.0</Real>
<Real>1.0</Real>
</Vector>
<Vector>
<Real>1.0</Real>
<Real>1.0</Real>
<Real>1.0</Real>
</Vector>
</Sun>
<Sun>
<Vector>
<Real>-0.866</Real>
<Real>-0.5</Real>
<Real>0.05</Real>
</Vector>
<Vector>
<Real>0.8</Real>
<Real>0.8</Real>
<Real>0.4</Real>
</Vector>
</Sun>
<Sun>
<Vector>
<Real>-1.0</Real>
<Real>0.2</Real>
<Real>1.0</Real>
</Vector>
<Vector>
<Real>0.5</Real>
<Real>1.0</Real>
<Real>1.0</Real>
</Vector>
</Sun>
<Defaults>
<Parameters>
<Vector>
<Real>0.4</Real>
<Real>0.4</Real>
<Real>0.4</Real>
</Vector>
<Vector>
<Real>0.0</Real>
<Real>0.0</Real>
<Real>0.0</Real>
</Vector>
<Real>0.2</Real>
<Real>10.0</Real>
<Real>0.6</Real>
</Parameters>
</Defaults>
<Plane>
<Vector>
<Real>4.0</Real>
<Real>0.0</Real>
<Real>0.0</Real>
</Vector>
<Vector>
<Real>0.0</Real>
<Real>4.0</Real>
<Real>0.0</Real>
</Vector>
<Vector>
<Real>0.0</Real>
<Real>0.0</Real>
<Real>-2.0</Real>
</Vector>
</Plane>
<Defaults>
<Parameters>
<Vector>
<Real>0.7</Real>
<Real>0.0</Real>
<Real>0.0</Real>
</Vector>
<Vector>
<Real>0.0</Real>
<Real>0.1</Real>
<Real>0.0</Real>
</Vector>
<Real>1.2</Real>
<Real>9.0</Real>
</Parameters>
</Defaults>
<Triangle>
<Vector>
<Real>0.0</Real>
<Real>0.0</Real>
<Real>1.5</Real>
</Vector>
<Vector>
<Real>0.0</Real>
<Real>1.0</Real>
<Real>0.0</Real>
</Vector>
<Vector>
<Real>-0.866</Real>
<Real>-0.5</Real>
<Real>0.0</Real>
</Vector>
</Triangle>
<Defaults>
<Parameters>
<Vector>
<Real>0.7</Real>
<Real>0.0</Real>
<Real>0.0</Real>
</Vector>
<Vector>
<Real>0.3</Real>
<Real>0.1</Real>
<Real>0.0</Real>
</Vector>
</Parameters>
</Defaults>
<Triangle>
<Vector>
<Real>0.0</Real>
<Real>0.0</Real>
<Real>1.5</Real>
</Vector>
<Vector>
<Real>-0.866</Real>
<Real>-0.5</Real>
<Real>0.0</Real>
</Vector>
<Vector>
<Real>0.866</Real>
<Real>-0.5</Real>
<Real>0.0</Real>
</Vector>
</Triangle>
<Defaults>
<Parameters>
<Vector>
<Real>0.7</Real>
<Real>0.0</Real>
<Real>0.0</Real>
</Vector>
<Vector>
<Real>0.6</Real>
<Real>0.1</Real>
<Real>0.0</Real>
</Vector>
</Parameters>
</Defaults>
<Triangle>
<Vector>
<Real>0.0</Real>
<Real>0.0</Real>
<Real>1.5</Real>
</Vector>
<Vector>
<Real>0.866</Real>
<Real>-0.5</Real>
<Real>0.0</Real>
</Vector>
<Vector>
<Real>0.0</Real>
<Real>1.0</Real>
<Real>0.0</Real>
</Vector>
</Triangle>
<Defaults>
<Parameters>
<Vector>
<Real>0.7</Real>
<Real>0.0</Real>
<Real>0.0</Real>
</Vector>
<Vector>
<Real>0.9</Real>
<Real>0.1</Real>
<Real>0.0</Real>
</Vector>
</Parameters>
</Defaults>
<Triangle>
<Vector>
<Real>0.0</Real>
<Real>1.0</Real>
<Real>0.0</Real>
</Vector>
<Vector>
<Real>-0.866</Real>
<Real>-0.5</Real>
<Real>0.0</Real>
</Vector>
<Vector>
<Real>0.866</Real>
<Real>-0.5</Real>
<Real>0.0</Real>
</Vector>
</Triangle>
</Tinyray>