Zeichenstrom -> Tokenstrom.
Tinyray { Background { [1,0.5,0] } Sphere { [0,0,0]; 1 } Sun { [1,1,1]; [1,1,1] } }
->
TINYRAY LBRACE BACKGROUND LBRACE LBRACKET REAL SEP REAL SEP REAL RBRACKET RBRACE SPHERE LBRACE LBRACKET REAL SEP REAL SEP REAL RBRACKET SEP REAL RBRACE SUN LBRACE LBRACKET REAL SEP REAL SEP REAL RBRACKET SEP LBRACKET REAL SEP REAL SEP REAL RBRACKET RBRACE RBRACE
Token[type=TINYRAY; location=(0,0); value='Tinyray']
Token[type=LBRACE; location=(8,0); value='{']
Token[type=BACKGROUND; location=(10,0); value='Background']
Token[type=LBRACE; location=(21,0); value='{']
Token[type=LBRACE; location=(23,0); value='[']
Token[type=REAL; location=(24,0); value='1']
Token[type=SEP; location=(25,0); value=',']
Token[type=REAL; location=(26,0); value='0.5']
Token[type=SEP; location=(29,0); value=',']
Token[type=REAL; location=(30,0); value='0']
Token[type=RBRACKET; location=(31,0); value=']']
...
„type”,
„location”
und
„value”
werden in der
syntaktischen Analyse
wie folgt verwendet:
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.
package tinyray.frontend;
/**
* Die Klasse Location kapselt die Positionsangabe eines
* Text-Cursors während eines Lesevorgangs in einem Codeabschnitt.
* Gelesen wird zeichenweise aus einem Byte-Strom; interpretiert
* man allerdings den Byte-Strom als Source-Code, dann gibt es
* Zeilen und Spalten. Und wenn man den Source-Code von links nach
* rechts und von oben nach unten liest, so wie man eben einen
* Source-Code liest. Und nach jedem gelesenen Zeichen
* location.incPosition() und nach jedem gelesenen Zeilenumbruch
* location.newLine() aufruft, repräsentiert die Location genau
* die aktuelle Leseposition des Lesenden. Der Lesende ist in
* diesem Falle der Scanner. Entsteht zum Beispiel beim Parsen eine
* ParseException, dann kann mit Hilfe der Location vielleicht sogar
* die exakte Position des Fehlers im Source-Code zurückgeben;
* dies hilft besonders bei der Korrektur eines Fehlers in einem Source-Code.
*/
public class Location {
// Anzahl der gezählten Zeichen.
private long charNumber;
// Anzahl der gezählten Zeilen.
private long lineNumber;
// Anzahl der gezählten Zeichen der letzten Zeile.
private long charNumberOfLine;
/**
* Initialisiert die Cursor-Position mit der Startposition (0, 0).
*/
public Location() {
this.charNumber = 0;
this.lineNumber = 0;
this.charNumberOfLine = 0;
}
/**
* Klont die aktuelle Cursor-Position.
* @return Der Klon.
*/
public Location copy() {
Location result = new Location();
result.charNumber = this.charNumber;
result.lineNumber = this.lineNumber;
result.charNumberOfLine = this.charNumberOfLine;
return result;
}
/**
* Gibt die Anzahl der bereits gezählten Zeichen zurück.
* @return Die Anzahl der gezählten Zeichen.
*/
public long getCharNumber() {
return this.charNumber;
}
/**
* Gibt die Anzahl der bereits gezählten Zeilen zurück.
* @return Die Anzahl der gezählten Zeilen.
*/
public long getLineNumber() {
return this.lineNumber;
}
/**
* Gibt die Anzahl der gezählten Zeichen der letzten Zeile zurück.
* @return Die Anzahl der gezählten Zeichen der letzten Zeile.
*/
public long getCharNumberOfLine() {
return this.charNumberOfLine;
}
/**
* Die Cursor-Position wird um ein Zeichen nach rechts versetzt.
*/
public void incPosition() {
this.charNumber++;
this.charNumberOfLine++;
}
/**
* Die Cursor-Position wird um count-viele Zeichen nach rechts versetzt.
* @param count Die Anzahl der Zeichen, um die inkrementiert wird.
*/
public void incPosition(long count) {
this.charNumber += count;
this.charNumberOfLine += count;
}
/**
* Die Cursor-Position wird an den Anfang der nächsten Zeile gesetzt.
*/
public void newLine() {
this.lineNumber++;
this.charNumberOfLine = 0;
}
/**
* Gibt die textuelle Repräsentation der aktuellen Cursor-Position
* zurück.
*/
public String toString() {
return "[" + this.lineNumber + ":" + this.charNumberOfLine + "]";
}
}
package tinyray.frontend;
/**
* Die Klasse Token stellt ein Terminalsymbol der Sprache Tinyray dar.
*/
public class Token {
/**
* Enum Type bezeichnet den Typ eines Tokens.
*/
public static enum Type {
/** Bezeichnet eine Fließkommazahl. */
REAL,
/** Die Zeichenkette "Tinyray". */
TINYRAY,
/** Die Zeichenkette "Camera". */
CAMERA,
/** Die Zeichenkette "Bounding". */
BOUNDING,
/** Die Zeichenkette "Triangle". */
TRIANGLE,
/** Die Zeichenkette "Sphere". */
SPHERE,
/** Die Zeichenkette "Plane". */
PLANE,
/** Die Zeichenkette "Sun". */
SUN,
/** Die Zeichenkette "Background". */
BACKGROUND,
/** Die Zeichenkette "Ambience". */
AMBIENCE,
/** Die Zeichenkette "Fog". */
FOG,
/** Die Zeichenkette "Defaults". */
DEFAULTS,
/** Die öffnende, geschweifte Klammer "{". */
LBRACE,
/** Die schließende, geschweifte Klammer "}". */
RBRACE,
/** Die öffnende, eckige Klammer "[". */
LBRACKET,
/** Die schließende, eckige Klammer "]". */
RBRACKET,
/** Die Trennzeichen "," und ";". */
SEP,
/** Der Zeilenendekommentar "//...". Kein Blockkommentar! */
COMMENT,
/** Unbekanntes Token. */
UNKNOWN,
/**
* Bezeichnet das Ende der zu lesenden Daten.
* EOS steht für "End-Of-String" bzw. "End-Of-Stream".
*/
EOS
}
// Der Typ des Token.
private Token.Type type;
// Der textuelle Wert des Token.
private String value;
// Die Position des Token im Source-Code.
private Location location;
/**
* Initialisiert das Token mit der Default-Einstellung.
*/
public Token() {
this.type = Type.UNKNOWN;
this.value = "";
this.location = new Location();
}
/**
* Klont das Token.
* @return Der Klon.
*/
public Token copy() {
Token result = new Token();
result.type = this.type;
result.value = this.value;
result.location = this.location.copy();
return result;
}
/**
* Gibt den Typ des Token zurück.
* @return Der Typ des Token.
*/
public Token.Type getType() {
return this.type;
}
/**
* Setzt den Typ des Token.
* @param type Der Typ des Token.
*/
public void setType(Token.Type type) {
this.type = type;
}
/**
* Gibt den Wert des Token zurück.
* @return Der Wert des Token.
*/
public String getValue() {
return this.value;
}
/**
* Setzt den textuellen Wert des Token.
* @param value Der Wert des Token.
*/
public void setValue(String value) {
this.value = value;
}
/**
* Setzt den textuellen Wert des Token.
* Da es in der Sprache Tinyray viele Tokens gibt, die nur ein Zeichen
* lang sind, kann auch der Wert des Token mit einem einzelnen Zeichen
* gesetzt werden.
* @param value Der Wert des Token.
*/
public void setValue(char value) {
this.value = "" + value;
}
/**
* Gibt die Anfangsposition des Token im Source-Code zurück.
* @return Die Anfangsposition des Token.
*/
public Location getLocation() {
return this.location;
}
/**
* Setzt die Anfangsposition des Token.
* @param location Die Anfangsposition des Token.
*/
public void setLocation(Location location) {
this.location = location.copy();
}
/**
* Gibt die textuelle Repräsentation des Token zurück.
*/
public String toString() {
return this.type + " \"" + this.value + "\" in " + this.location;
}
}
getToken()
nextToken()
package tinyray.frontend;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Die Klasse Scanner wird dazu benutzt, den Zeichenstrom eines
* Tinyray-Programms in einen Token-Strom umzuwandeln.
* Der Scanner ist somit der Wegbereiter für den Parse-Vorgang.
*/
public class Scanner {
/**
* Initialisiert den Scanner mit einem Tinyray-Programm in
* Form eines Eingabestroms.
* @param inputStream Das Tinyray-Programm als Eingabestrom.
*/
public Scanner(InputStream inputStream) {
this.stream = new BufferedInputStream(inputStream);
this.token = new Token();
this.location = new Location();
}
/**
* Initialisiert den Scanner mit einem Tinyray-Programm in
* Form einer Zeichenkette.
* @param code Das Tinyray-Programm als Zeichenkette.
*/
public Scanner(String code) {
this(new ByteArrayInputStream(code.getBytes()));
}
/**
* Gibt eine Kopie des zuletzt gelesenen (aktuellen) Token zurück.
* @return Die Kopie des aktuellen Token.
*/
public Token getToken() {
return this.token.copy();
}
/**
* Liest das nächste Token im Tinyray-Programm.
* Diese Methode ist das Herz des Scanners, welche es ermöglicht den
* Source-Code des Tinyray-Programms tokenweise zu lesen. Der Source-Code
* selbst kann keine Exception auslösen, da aber der Source-Code in Form
* eines beliebigen InputStreams vorliegen kann, so besteht eine
* potentielle Gefahr einer IOException.
* @throws IOException Die IOException.
*/
public void nextToken() throws IOException {
// Es wird die aktuelle Leseposition im aktuellen Token gesichert.
this.token.setLocation(this.location);
// Diese Schleife terminiert nur dann, wenn ein Token erkannt wird.
while (this.canRead()) {
switch (this.peek()) {
// neue Zeile -> Position aktualisieren.
case '\n':
this.read();
this.location.newLine();
this.token.setLocation(this.location);
break;
// weitere Whitespaces -> Position aktualisieren
case '\r':
case '\t':
case ' ':
this.read();
this.token.setLocation(this.location);
break;
case '{':
this.token.setType(Token.Type.LBRACE);
this.token.setValue(this.read());
return; // Token LBRACE(öffnende, geschweifte Klammer) erkannt.
case '}':
this.token.setType(Token.Type.RBRACE);
this.token.setValue(this.read());
return; // Token RBRACE erkannt.
case '[':
this.token.setType(Token.Type.LBRACKET);
this.token.setValue(this.read());
return; // Token LBRACKET erkannt.
case ']':
this.token.setType(Token.Type.RBRACKET);
this.token.setValue(this.read());
return; // Token RBRACKET erkannt.
case ',':
case ';':
this.token.setType(Token.Type.SEP);
this.token.setValue(this.read());
return; // Token SEP erkannt.
// Ganzzahl oder Fließkommazahl scannen
case '+':
case '-':
case '.':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
// Möglicherweise liegt eine Zahl an.
// Um eine Zahl zu erkennen, benötigt man verhältnismäßig
// viel Aufwand, deshalb wird das Scannen einer Zahl in eine
// weitere Methode ausgelagert.
this.scanReal();
return; // REAL gescannt.
case '/':
// Möglicherweise liegt ein Kommentar an.
this.scanComment();
return; // COMMENT gescannt.
default:
// Prüft, ob bestimmte Zeichenketten anliegen, andernfalls
// wird der Typ des Token auf UNKNOWN gesetzt.
if (this.hasCode(CODE_TINYRAY)) {
this.token.setType(Token.Type.TINYRAY);
this.token.setValue(this.read(CODE_TINYRAY.length()));
return; // Token TINYRAY erkannt.
} else if (this.hasCode(CODE_CAMERA)) {
this.token.setType(Token.Type.CAMERA);
this.token.setValue(this.read(CODE_CAMERA.length()));
return; // Token CAMERA erkannt.
} else if (this.hasCode(CODE_BOUNDING)) {
this.token.setType(Token.Type.BOUNDING);
this.token.setValue(this.read(CODE_BOUNDING.length()));
return; // Token BOUNDING erkannt.
} else if (this.hasCode(CODE_TRIANGLE)) {
this.token.setType(Token.Type.TRIANGLE);
this.token.setValue(this.read(CODE_TRIANGLE.length()));
return; // Token TRIANGLE erkannt.
} else if (this.hasCode(CODE_SPHERE)) {
this.token.setType(Token.Type.SPHERE);
this.token.setValue(this.read(CODE_SPHERE.length()));
return; // Token SPHERE erkannt.
} else if (this.hasCode(CODE_PLANE)) {
this.token.setType(Token.Type.PLANE);
this.token.setValue(this.read(CODE_PLANE.length()));
return; // Token PLANE erkannt.
} else if (this.hasCode(CODE_SUN)) {
this.token.setType(Token.Type.SUN);
this.token.setValue(this.read(CODE_SUN.length()));
return; // Token SUN erkannt.
} else if (this.hasCode(CODE_BACKGROUND)) {
this.token.setType(Token.Type.BACKGROUND);
this.token.setValue(this.read(CODE_BACKGROUND.length()));
return; // Token BACKGROUND erkannt.
} else if (this.hasCode(CODE_AMBIENCE)) {
this.token.setType(Token.Type.AMBIENCE);
this.token.setValue(this.read(CODE_AMBIENCE.length()));
return; // Token AMBIENCE erkannt.
} else if (this.hasCode(CODE_FOG)) {
this.token.setType(Token.Type.FOG);
this.token.setValue(this.read(CODE_FOG.length()));
return; // Token FOG erkannt.
} else if (this.hasCode(CODE_DEFAULTS)) {
this.token.setType(Token.Type.DEFAULTS);
this.token.setValue(this.read(CODE_DEFAULTS.length()));
return; // Token DEFAULTS erkannt.
} else {
this.token.setType(Token.Type.UNKNOWN);
this.token.setValue(this.read());
return;
}
}
}
// Dieser Teil wird nur erreicht, wenn die Schleife nicht besucht
// wird, weil eben kein Zeichen mehr gelesen werden kann, also das
// Ende des Zeichen-Stroms erreicht ist.
this.token.setType(Token.Type.EOS);
this.token.setValue("");
return;
}
/**
* Gibt die textuelle Repräsentation des Scanners zurück.
*/
public String toString() {
return "Scanner { " + this.getToken() + " }";
}
/* Privater Teil */
// Terminale Zeichenketten der Sprache Tinyray.
private final static String CODE_TINYRAY = "Tinyray";
private final static String CODE_CAMERA = "Camera";
private final static String CODE_BOUNDING = "Bounding";
private final static String CODE_TRIANGLE = "Triangle";
private final static String CODE_SPHERE = "Sphere";
private final static String CODE_PLANE = "Plane";
private final static String CODE_SUN = "Sun";
private final static String CODE_BACKGROUND = "Background";
private final static String CODE_AMBIENCE = "Ambience";
private final static String CODE_FOG = "Fog";
private final static String CODE_DEFAULTS = "Defaults";
// Der gepufferte Eingabestrom.
private BufferedInputStream stream;
// Das aktuelle Token.
private Token token;
// Die aktuelle Leseposition.
private Location location;
// Prüft, ob noch ein Zeichen anliegt.
private boolean canRead() throws IOException {
return this.stream.available() >= 1;
}
// Prüft, ob noch count viele Zeichen anliegen.
private boolean canRead(int count) throws IOException {
return this.stream.available() >= count;
}
// Liest das nächste Zeichen.
private char read() throws IOException {
this.location.incPosition();
return (char)this.stream.read();
}
// Liest die nächsten count-Zeichen.
private String read(int count) throws IOException {
byte[] bytes = new byte[Math.max(0, count)];
this.stream.read(bytes, 0, bytes.length);
this.location.incPosition(bytes.length);
return new String(bytes);
}
// Gibt das nächste Zeichen zurück, ohne die Leseposition zu verändern.
private char peek() throws IOException {
char result = (char)0;
if (this.canRead()) {
this.stream.mark(1);
result = (char)this.stream.read();
this.stream.reset();
}
return result;
}
// Gibt die nächten count-Zeichen zurück, ohne die Leseposition zu
// verändern.
private String peek(int count) throws IOException {
String result = "";
while ((count > 0) && (!this.canRead(count))) {
count--;
}
if (count > 0) {
this.stream.mark(count);
byte[] bytes = new byte[Math.max(0, count)];
this.stream.read(bytes, 0, bytes.length);
result = new String(bytes);
this.stream.reset();
}
return result;
}
// Prüft, ob gegebener String anliegt.
private boolean hasCode(String code) throws IOException {
return code.equals(this.peek(code.length()));
}
// Prüft, ob eine Ziffer anliegt.
private boolean hasDigit() throws IOException {
return Character.isDigit(this.peek());
}
// Scannt eine Zahl.
private void scanReal() throws IOException {
Token.Type type = Token.Type.UNKNOWN;
String number = "";
// Vorzeichen lesen (optional)
if ((this.hasCode("+")) || (this.hasCode("-"))) {
number += this.read();
}
// Ziffernfolge lesen (optional)
while (this.hasDigit()) {
type = Token.Type.REAL; // bis hier ein INTEGER
number += this.read();
}
// Punkt mit Ziffernfolge lesen (optional)
if (this.hasCode(".")) {
type = Token.Type.UNKNOWN;
number += this.read();
while (this.hasDigit()) {
type = Token.Type.REAL;
number += this.read();
}
}
// Exponent lesen (optional)
if (type == Token.Type.REAL) {
if ((this.hasCode("E")) || (this.hasCode("e"))) {
type = Token.Type.UNKNOWN;
number += this.read();
// Vorzeichen des Exponenten
if ((this.hasCode("+")) || (this.hasCode("-"))) {
number += this.read();
}
// Exponent
while (this.hasDigit()) {
type = Token.Type.REAL;
number += this.read();
}
}
}
// Kann eigentlich nicht auftreten, vielleicht zu vorsichtig!
if (number.length() <= 0) {
type = Token.Type.UNKNOWN;
number += this.read();
}
this.token.setType(type);
this.token.setValue(number);
}
// Scannt einen Zeilenendekommentar.
private void scanComment() throws IOException {
Token.Type type = Token.Type.UNKNOWN;
String comment = "";
if (this.hasCode("//")) {
type = Token.Type.COMMENT;
comment += this.read(2);
while (this.canRead()) {
if (this.peek() == '\n') {
break;
} else {
comment += this.read();
}
}
}
this.token.setType(type);
this.token.setValue(comment);
}
}
s
im Prinzip wie einen Iterator.
Der Anwender,
im häufigsten Falle der Parser,
iteriert mit dem Scanner über dem Tinyray-Quellprogramm nicht zeichenweise, sondern tokenweise.
...
s = new Scanner(sourcecode)
do {
token = s.next()
tue_was_mit(token)
} while (token != EOS) // hasNext?
...
package tinyray.examples;
import java.io.IOException;
import java.util.ArrayList;
import tinyray.frontend.Location;
import tinyray.frontend.Scanner;
import tinyray.frontend.Token;
/**
* Anwendungsbeispiel des Scanners.
*/
public class Scan {
/**
* Hauptprogramm des Scanneranwendungsbeispiels.
* @param arguments Tinyray-Quellprogramm als Argument erwartet.
* @throws IOException Der Scan-Vorgang kann eine IOException auslösen.
*/
public static void main(String[] arguments) throws IOException {
// Das Tinyray-Quellprogramm in "code" sichern.
String code = "Tinyray{}";
if (arguments.length > 0) {
code = arguments[0];
}
// Mit Hilfe des Scanners wird aus dem "code"
// ein Tokenstrom "tokenStream" gemacht.
ArrayList<Token> tokenStream = new ArrayList<Token>();
Scanner scanner = new Scanner(code);
do {
scanner.nextToken();
tokenStream.add(scanner.getToken());
} while (scanner.getToken().getType() != Token.Type.EOS);
// Ausgabe des Tokenstromes "tokenStream"
for (int i = 0; i < tokenStream.size(); i++) {
Token token = tokenStream.get(i);
Token.Type type = token.getType();
Location location = token.getLocation();
String value = token.getValue();
String tokenInfo = (i + 1) + ". Token[";
tokenInfo += "type=" + type + "; ";
tokenInfo += "location=(" + location.getCharNumberOfLine() + ",";
tokenInfo += location.getLineNumber() + "); ";
tokenInfo += "value='" + value + "'";
tokenInfo += "]";
System.out.println(tokenInfo);
}
}
}
„Tinyray{}”
1. Token[type=TINYRAY; location=(0,0); value='Tinyray']
2. Token[type=LBRACE; location=(7,0); value='{']
3. Token[type=RBRACE; location=(8,0); value='}']
4. Token[type=EOS; location=(9,0); value='']
„Tinyray{Sphere{[0.3,0,1];2.0}}”
1. Token[type=TINYRAY; location=(0,0); value='Tinyray']
2. Token[type=LBRACE; location=(7,0); value='{']
3. Token[type=SPHERE; location=(8,0); value='Sphere']
4. Token[type=LBRACE; location=(14,0); value='{']
5. Token[type=LBRACKET; location=(15,0); value='[']
6. Token[type=REAL; location=(16,0); value='0.3']
7. Token[type=SEP; location=(19,0); value=',']
8. Token[type=REAL; location=(20,0); value='0']
9. Token[type=SEP; location=(21,0); value=',']
10. Token[type=REAL; location=(22,0); value='1']
11. Token[type=RBRACKET; location=(23,0); value=']']
12. Token[type=SEP; location=(24,0); value=';']
13. Token[type=REAL; location=(25,0); value='2.0']
14. Token[type=RBRACE; location=(28,0); value='}']
15. Token[type=RBRACE; location=(29,0); value='}']
16. Token[type=EOS; location=(30,0); value='']
package tinyray.examples;
import java.io.IOException;
import tinyray.frontend.Scanner;
import tinyray.frontend.Token;
/**
* Kurzes Anwendungsbeispiel des Scanners.
*/
public class ScanShort {
/**
* Hauptprogramm des Scanneranwendungsbeispiels.
* @param arguments Tinyray-Quellprogramm als Argument erwartet.
* @throws IOException Der Scan-Vorgang kann eine IOException auslösen.
*/
public static void main(String[] arguments) throws IOException {
// Das Tinyray-Quellprogramm in "code" sichern.
String code = "Tinyray{}";
if (arguments.length > 0) {
code = arguments[0];
}
/* The same procedure as every year :-) */
// Die Instanz des Scanners erhält einen Tinyray-Code.
Scanner scanner = new Scanner(code);
// Solange der Scanner nicht das Ende vom Tinyray-Code erreicht
// wurde, wird das nächste Token gescannt und anschließend ausgegeben.
do {
scanner.nextToken();
System.out.println(scanner.getToken());
} while (scanner.getToken().getType() != Token.Type.EOS);
}
}
TINYRAY "Tinyray" in [0:0]
LBRACE "{" in [0:7]
RBRACE "}" in [0:8]
EOS "" in [0:9]
TINYRAY "Tinyray" in [0:0]
LBRACE "{" in [0:7]
SPHERE "Sphere" in [0:8]
LBRACE "{" in [0:14]
LBRACKET "[" in [0:15]
REAL "0.3" in [0:16]
SEP "," in [0:19]
REAL "0" in [0:20]
SEP "," in [0:21]
REAL "1" in [0:22]
RBRACKET "]" in [0:23]
SEP ";" in [0:24]
REAL "2.0" in [0:25]
RBRACE "}" in [0:28]
RBRACE "}" in [0:29]
EOS "" in [0:30]
package tinyray.examples;
import java.io.FileInputStream;
import java.io.IOException;
import tinyray.frontend.Scanner;
import tinyray.frontend.Token;
/**
* Kurzes Anwendungsbeispiel des Scanners, welches eine Datei scannt.
*/
public class ScanFile {
/**
* Hauptprogramm des Scanneranwendungsbeispiels.
* @param arguments Dateiname als Argument erwartet.
* @throws IOException Der Scan-Vorgang kann eine IOException auslösen.
*/
public static void main(String[] arguments) throws IOException {
// Der Dateinamen eines Tinyray-Quellprogramms.
String filename = "hello-world.tinyray";
if (arguments.length > 0) {
filename = arguments[0];
}
/* The same procedure as every year :-) */
// Die Instanz des Scanners erhält einen Tinyray-Code.
Scanner scanner = new Scanner(new FileInputStream(filename));
// Solange der Scanner nicht das Ende vom Tinyray-Code erreicht
// wurde, wird das nächste Token gescannt und anschließend ausgegeben.
do {
scanner.nextToken();
System.out.println(scanner.getToken());
} while (scanner.getToken().getType() != Token.Type.EOS);
}
}
// 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?
}
}
COMMENT "// Hallo Welt!" in [0:0]
TINYRAY "Tinyray" in [2:0]
LBRACE "{" in [2:8]
SPHERE "Sphere" in [3:4]
LBRACE "{" in [3:11]
LBRACKET "[" in [4:8]
REAL "0" in [4:9]
SEP "," in [4:10]
REAL "0" in [4:12]
SEP "," in [4:13]
REAL "0" in [4:15]
RBRACKET "]" in [4:16]
SEP ";" in [4:17]
COMMENT "// Wo liegt die Kugel?" in [4:19]
REAL "2.0" in [5:8]
COMMENT "// Welchen Radius besitzt die Kugel?" in [5:19]
RBRACE "}" in [6:4]
SUN "Sun" in [7:4]
LBRACE "{" in [7:8]
LBRACKET "[" in [8:8]
REAL "1" in [8:9]
SEP "," in [8:10]
REAL "1" in [8:12]
SEP "," in [8:13]
REAL "1" in [8:15]
RBRACKET "]" in [8:16]
SEP ";" in [8:17]
COMMENT "// In welcher Richtung befindet sich die Sonne?" in [8:19]
LBRACKET "[" in [9:8]
REAL "1" in [9:9]
SEP "," in [9:10]
REAL "1" in [9:12]
SEP "," in [9:13]
REAL "1" in [9:15]
RBRACKET "]" in [9:16]
COMMENT "// Welche Farbe hat die Sonne?" in [9:19]
RBRACE "}" in [10:4]
RBRACE "}" in [11:0]
EOS "" in [11:1]
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] }
}
„pyramid.tinyray”
mit einem TAB-Zeichen gesetzt sind.
Ein TAB-Zeichen wird hier als normales Zeichen gelesen.
TINYRAY "Tinyray" in [0:0]
LBRACE "{" in [0:8]
CAMERA "Camera" in [1:1]
LBRACE "{" in [1:8]
LBRACKET "[" in [1:10]
REAL "0.0" in [1:11]
SEP "," in [1:14]
REAL "0.0" in [1:16]
SEP "," in [1:19]
REAL "9.0" in [1:21]
RBRACKET "]" in [1:24]
SEP ";" in [1:25]
LBRACKET "[" in [1:27]
REAL "1.6" in [1:28]
SEP "," in [1:31]
REAL "1.1" in [1:33]
SEP "," in [1:36]
REAL "0.0" in [1:38]
RBRACKET "]" in [1:41]
SEP ";" in [1:42]
LBRACKET "[" in [1:44]
REAL "0.0" in [1:45]
SEP "," in [1:48]
REAL "1.0" in [1:50]
SEP "," in [1:53]
REAL "0.0" in [1:55]
RBRACKET "]" in [1:58]
RBRACE "}" in [1:60]
AMBIENCE "Ambience" in [2:1]
LBRACE "{" in [2:10]
LBRACKET "[" in [2:12]
REAL "0.4" in [2:13]
SEP "," in [2:16]
REAL "0.6" in [2:18]
SEP "," in [2:21]
REAL "0.0" in [2:23]
RBRACKET "]" in [2:26]
RBRACE "}" in [2:28]
BACKGROUND "Background" in [3:1]
LBRACE "{" in [3:12]
LBRACKET "[" in [3:14]
REAL "0.0" in [3:15]
SEP "," in [3:18]
REAL "0.4" in [3:20]
SEP "," in [3:23]
REAL "0.7" in [3:25]
RBRACKET "]" in [3:28]
RBRACE "}" in [3:30]
FOG "Fog" in [4:1]
LBRACE "{" in [4:5]
LBRACKET "[" in [4:7]
REAL "0.3" in [4:8]
SEP "," in [4:11]
REAL "0.3" in [4:13]
SEP "," in [4:16]
REAL "0.8" in [4:18]
RBRACKET "]" in [4:21]
SEP ";" in [4:22]
REAL "0.065" in [4:24]
RBRACE "}" in [4:30]
SUN "Sun" in [6:1]
LBRACE "{" in [6:5]
LBRACKET "[" in [6:7]
REAL "1.0" in [6:8]
SEP "," in [6:11]
REAL "1.0" in [6:13]
SEP "," in [6:16]
REAL "1.0" in [6:18]
RBRACKET "]" in [6:21]
SEP ";" in [6:22]
LBRACKET "[" in [6:24]
REAL "1.0" in [6:25]
SEP "," in [6:28]
REAL "1.0" in [6:30]
SEP "," in [6:33]
REAL "1.0" in [6:35]
RBRACKET "]" in [6:38]
RBRACE "}" in [6:40]
SUN "Sun" in [7:1]
LBRACE "{" in [7:5]
LBRACKET "[" in [7:7]
REAL "-0.866" in [7:8]
SEP "," in [7:14]
REAL "-0.5" in [7:16]
SEP "," in [7:20]
REAL "0.05" in [7:22]
RBRACKET "]" in [7:26]
SEP ";" in [7:27]
LBRACKET "[" in [7:29]
REAL "0.8" in [7:30]
SEP "," in [7:33]
REAL "0.8" in [7:35]
SEP "," in [7:38]
REAL "0.4" in [7:40]
RBRACKET "]" in [7:43]
RBRACE "}" in [7:45]
SUN "Sun" in [8:1]
LBRACE "{" in [8:5]
LBRACKET "[" in [8:7]
REAL "-1.0" in [8:8]
SEP "," in [8:12]
REAL "0.2" in [8:14]
SEP "," in [8:17]
REAL "1.0" in [8:19]
RBRACKET "]" in [8:22]
SEP ";" in [8:23]
LBRACKET "[" in [8:25]
REAL "0.5" in [8:26]
SEP "," in [8:29]
REAL "1.0" in [8:31]
SEP "," in [8:34]
REAL "1.0" in [8:36]
RBRACKET "]" in [8:39]
RBRACE "}" in [8:41]
DEFAULTS "Defaults" in [10:1]
LBRACE "{" in [10:10]
LBRACKET "[" in [10:12]
REAL "0.4" in [10:13]
SEP "," in [10:16]
REAL "0.4" in [10:18]
SEP "," in [10:21]
REAL "0.4" in [10:23]
RBRACKET "]" in [10:26]
SEP ";" in [10:27]
LBRACKET "[" in [10:29]
REAL "0.0" in [10:30]
SEP "," in [10:33]
REAL "0.0" in [10:35]
SEP "," in [10:38]
REAL "0.0" in [10:40]
RBRACKET "]" in [10:43]
SEP ";" in [10:44]
REAL "0.2" in [10:46]
SEP ";" in [10:49]
REAL "10.0" in [10:51]
SEP ";" in [10:55]
REAL "0.6" in [10:57]
RBRACE "}" in [10:61]
PLANE "Plane" in [11:1]
LBRACE "{" in [11:7]
LBRACKET "[" in [11:9]
REAL "4.0" in [11:10]
SEP "," in [11:13]
REAL "0.0" in [11:15]
SEP "," in [11:18]
REAL "0.0" in [11:20]
RBRACKET "]" in [11:23]
SEP ";" in [11:24]
LBRACKET "[" in [11:26]
REAL "0.0" in [11:27]
SEP "," in [11:30]
REAL "4.0" in [11:32]
SEP "," in [11:35]
REAL "0.0" in [11:37]
RBRACKET "]" in [11:40]
SEP ";" in [11:41]
LBRACKET "[" in [11:43]
REAL "0.0" in [11:44]
SEP "," in [11:47]
REAL "0.0" in [11:49]
SEP "," in [11:52]
REAL "-2.0" in [11:54]
RBRACKET "]" in [11:58]
RBRACE "}" in [11:60]
DEFAULTS "Defaults" in [13:1]
LBRACE "{" in [13:10]
LBRACKET "[" in [13:12]
REAL "0.7" in [13:13]
SEP "," in [13:16]
REAL "0.0" in [13:18]
SEP "," in [13:21]
REAL "0.0" in [13:23]
RBRACKET "]" in [13:26]
SEP ";" in [13:27]
LBRACKET "[" in [13:29]
REAL "0.0" in [13:30]
SEP "," in [13:33]
REAL "0.1" in [13:35]
SEP "," in [13:38]
REAL "0.0" in [13:40]
RBRACKET "]" in [13:43]
SEP ";" in [13:44]
REAL "1.2" in [13:46]
SEP ";" in [13:49]
REAL "9.0" in [13:51]
RBRACE "}" in [13:55]
TRIANGLE "Triangle" in [14:1]
LBRACE "{" in [14:10]
LBRACKET "[" in [14:12]
REAL "0.0" in [14:13]
SEP "," in [14:16]
REAL "0.0" in [14:18]
SEP "," in [14:21]
REAL "1.5" in [14:23]
RBRACKET "]" in [14:26]
SEP ";" in [14:27]
LBRACKET "[" in [14:29]
REAL "0.0" in [14:30]
SEP "," in [14:33]
REAL "1.0" in [14:35]
SEP "," in [14:38]
REAL "0.0" in [14:40]
RBRACKET "]" in [14:43]
SEP ";" in [14:44]
LBRACKET "[" in [14:46]
REAL "-0.866" in [14:47]
SEP "," in [14:53]
REAL "-0.5" in [14:55]
SEP "," in [14:59]
REAL "0.0" in [14:61]
RBRACKET "]" in [14:64]
RBRACE "}" in [14:66]
DEFAULTS "Defaults" in [16:1]
LBRACE "{" in [16:10]
LBRACKET "[" in [16:12]
REAL "0.7" in [16:13]
SEP "," in [16:16]
REAL "0.0" in [16:18]
SEP "," in [16:21]
REAL "0.0" in [16:23]
RBRACKET "]" in [16:26]
SEP ";" in [16:27]
LBRACKET "[" in [16:29]
REAL "0.3" in [16:30]
SEP "," in [16:33]
REAL "0.1" in [16:35]
SEP "," in [16:38]
REAL "0.0" in [16:40]
RBRACKET "]" in [16:43]
RBRACE "}" in [16:45]
TRIANGLE "Triangle" in [17:1]
LBRACE "{" in [17:10]
LBRACKET "[" in [17:12]
REAL "0.0" in [17:13]
SEP "," in [17:16]
REAL "0.0" in [17:18]
SEP "," in [17:21]
REAL "1.5" in [17:23]
RBRACKET "]" in [17:26]
SEP ";" in [17:27]
LBRACKET "[" in [17:29]
REAL "-0.866" in [17:30]
SEP "," in [17:36]
REAL "-0.5" in [17:38]
SEP "," in [17:42]
REAL "0.0" in [17:44]
RBRACKET "]" in [17:47]
SEP ";" in [17:48]
LBRACKET "[" in [17:50]
REAL "0.866" in [17:51]
SEP "," in [17:56]
REAL "-0.5" in [17:58]
SEP "," in [17:62]
REAL "0.0" in [17:64]
RBRACKET "]" in [17:67]
RBRACE "}" in [17:69]
DEFAULTS "Defaults" in [19:1]
LBRACE "{" in [19:10]
LBRACKET "[" in [19:12]
REAL "0.7" in [19:13]
SEP "," in [19:16]
REAL "0.0" in [19:18]
SEP "," in [19:21]
REAL "0.0" in [19:23]
RBRACKET "]" in [19:26]
SEP ";" in [19:27]
LBRACKET "[" in [19:29]
REAL "0.6" in [19:30]
SEP "," in [19:33]
REAL "0.1" in [19:35]
SEP "," in [19:38]
REAL "0.0" in [19:40]
RBRACKET "]" in [19:43]
RBRACE "}" in [19:45]
TRIANGLE "Triangle" in [20:1]
LBRACE "{" in [20:10]
LBRACKET "[" in [20:12]
REAL "0.0" in [20:13]
SEP "," in [20:16]
REAL "0.0" in [20:18]
SEP "," in [20:21]
REAL "1.5" in [20:23]
RBRACKET "]" in [20:26]
SEP ";" in [20:27]
LBRACKET "[" in [20:29]
REAL "0.866" in [20:30]
SEP "," in [20:35]
REAL "-0.5" in [20:37]
SEP "," in [20:41]
REAL "0.0" in [20:43]
RBRACKET "]" in [20:46]
SEP ";" in [20:47]
LBRACKET "[" in [20:49]
REAL "0.0" in [20:50]
SEP "," in [20:53]
REAL "1.0" in [20:55]
SEP "," in [20:58]
REAL "0.0" in [20:60]
RBRACKET "]" in [20:63]
RBRACE "}" in [20:65]
DEFAULTS "Defaults" in [22:1]
LBRACE "{" in [22:10]
LBRACKET "[" in [22:12]
REAL "0.7" in [22:13]
SEP "," in [22:16]
REAL "0.0" in [22:18]
SEP "," in [22:21]
REAL "0.0" in [22:23]
RBRACKET "]" in [22:26]
SEP ";" in [22:27]
LBRACKET "[" in [22:29]
REAL "0.9" in [22:30]
SEP "," in [22:33]
REAL "0.1" in [22:35]
SEP "," in [22:38]
REAL "0.0" in [22:40]
RBRACKET "]" in [22:43]
RBRACE "}" in [22:45]
TRIANGLE "Triangle" in [23:1]
LBRACE "{" in [23:10]
LBRACKET "[" in [23:12]
REAL "0.0" in [23:13]
SEP "," in [23:16]
REAL "1.0" in [23:18]
SEP "," in [23:21]
REAL "0.0" in [23:23]
RBRACKET "]" in [23:26]
SEP ";" in [23:27]
LBRACKET "[" in [23:29]
REAL "-0.866" in [23:30]
SEP "," in [23:36]
REAL "-0.5" in [23:38]
SEP "," in [23:42]
REAL "0.0" in [23:44]
RBRACKET "]" in [23:47]
SEP ";" in [23:48]
LBRACKET "[" in [23:50]
REAL "0.866" in [23:51]
SEP "," in [23:56]
REAL "-0.5" in [23:58]
SEP "," in [23:62]
REAL "0.0" in [23:64]
RBRACKET "]" in [23:67]
RBRACE "}" in [23:69]
RBRACE "}" in [24:0]
EOS "" in [24:1]
„java ScanFile pyramid.tinyray”
extrem lang und geradezu unüberschaubar.
Doch dieser Output ist nicht für den Menschen gedacht, sondern für den
Parser.
Der Parser versucht diese
Token-Liste
(Tokenstrom)
mit der dazugehörigen Grammatik zu matchen.
package tinyray.examples;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import tinyray.frontend.Scanner;
import tinyray.frontend.Token;
public class ScannerIterator extends Scanner implements Iterator<Token> {
/* Konstruktoren übernehmen */
public ScannerIterator(InputStream inputStream) {
super(inputStream);
}
public ScannerIterator(String code) {
super(code);
}
/* Implementierung: Iterator<Token> */
public boolean hasNext() {
return this.getToken().getType() != Token.Type.EOS;
}
public Token next() {
try {
this.nextToken();
return this.getToken();
} catch (IOException e) {
System.err.println(e.getMessage());
return null;
}
}
public void remove() {
throw new UnsupportedOperationException();
}
/* Hauptprogramm */
public static void main(String[] arguments) throws IOException {
String filename = "hello-world.tinyray";
if (arguments.length > 0) {
filename = arguments[0];
}
ScannerIterator s = new ScannerIterator(new FileInputStream(filename));
while (s.hasNext()) {
Token token = s.next();
// Tue 'was mit dem Token
if (token != null) {
System.out.println(token);
} else {
break;
}
}
}
}