Startseite < Informatik < Algorithmen Datenstrukturen / Software-Engineering / Programmiersprachen < Compiler Interpreter < Setty Tinyray < [ DRUCK , 2004 , 2005 , 2006 , 2007 , 2008 , 2009 ] Tinyray-Scanner Tinyray-Parser / Tinyray-Language Tinyray-Raytracer / Tinyray-Wallpapers > > / Java C/C++ POV-Ray LaTeX > / Künstliche Intelligenz > Schach Privates / Inhalt >
Tinyray-Scanner
Der Scanner ist die Hauptkomponente der lexikalischen Analyse.
Lexikalische Analyse Die lexikalischen Analyse von Tinyray
Zielvorstellung
Ein Tinyray-Quellprogramm, welches als Zeichenstrom vorliegt, wird in einen Tokenstrom umgewandelt:
  1. Zeichenstrom -> Tokenstrom.
Das folgende Beispiel zeigt die Umwandlung eines Tinyray-Quellprogramms (Zeichenstrom) in einen entsprechenden Tokenstrom:
  1. Tinyray { Background { [1,0.5,0] } Sphere { [0,0,0]; 1 } Sun { [1,1,1]; [1,1,1] } }
  2. ->
  3. 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
Selbstverständlich werden zu jedem erkannten Token (type) die Position des Tokens (location) und die dazugehörige Zeichenkette (value) mitgespeichert, damit nach der lexikalischen Analyse das Tinyray-Quellprogramm als Zeichenstrom nicht mehr herangezogen werden muss. Die für die Folgephasen relevanten Informationen des Tinyray-Quellprogramms werden also vollständig in den Tokenstrom mitaufgenommen. Die Darstellung des Tokenstroms zum eben angeführten Beispiel würde im Detail etwa wie folgt aussehen:
  1. Token[type=TINYRAY; location=(0,0); value='Tinyray']
  2. Token[type=LBRACE; location=(8,0); value='{']
  3. Token[type=BACKGROUND; location=(10,0); value='Background']
  4. Token[type=LBRACE; location=(21,0); value='{']
  5. Token[type=LBRACE; location=(23,0); value='[']
  6. Token[type=REAL; location=(24,0); value='1']
  7. Token[type=SEP; location=(25,0); value=',']
  8. Token[type=REAL; location=(26,0); value='0.5']
  9. Token[type=SEP; location=(29,0); value=',']
  10. Token[type=REAL; location=(30,0); value='0']
  11. Token[type=RBRACKET; location=(31,0); value=']']
  12. ...
Die Attribute der Tokens „type”, „location” und „value” werden in der syntaktischen Analyse wie folgt verwendet:
  • Der Tokentyp (type) wird benötigt, um die Grammatik des Tinyray-Quellprogramms zu prüfen.
  • Falls das Tinyray-Quellprogramm irgendwelche Fehler aufweisen sollte, so kann mit der Position eines Tokens (location) eventuell die genaue Fehlerposition im Tinyray-Quellprogramm angezeigt werden.
  • Der Tokenwert (value) wird benötigt, um beispielsweise die Wertigkeit einer Fließkommazahl (REAL) ermitteln zu können.
Im Folgenden sei nochmals die Grammatik TG eines Tinyray-Quellprogramms angegeben.
Tinyray-Grammatik
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.
Implementierung Implementierung der lexikalischen Analyse von Tinyray
Für die lexikalische Analyse werden nur drei Dateien bzw. Klassen benötigt.
  1. Location.java
    Die Location repräsentiert eine Position (Zeile und Spalte) im Tinyray-Quellprogramm.
  2. Token.java
    Ein Token repräsentiert ein erkanntes Terminalsymbol des Tinyray-Quellprogramms.
  3. Scanner.java
    Die Kernkomponente der lexikalischen Analyse ist der Scanner. Der Scanner liest das Tinyray-Quellprogramm tokenweise und gibt dabei die erkannten Token zurück. 1 Während dem Lesen wird die aktuelle Leseposition stets aktualisiert.
Für diejenigen unter Ihnen, die es genau wissen wollen, folgen die Quelldateien der lexikalischen Analyse. Eine etwas einfachere Implementierung der lexikalischen Analyse, welche ohne Location realisiert ist, finden Sie unter Setty-Scanner.
Location.java
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 + "]";
    }
}
Token.java
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;
    }
}
Der Scanner ist die zentrale Komponente der lexikalischen Analyse und seine für den Anwender relevanten Methoden sind:
  • getToken()
    Zur Ermittlung des aktuell anliegenden Tokens.
  • nextToken()
    Liest im Quellprogramm das nächste Token.
Scanner.java
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);
    }
}
Anwendungsbeispiele Anwendungsbeispiele des Tinyray-Scanners
Wie die drei oben angeführten Klassen verwendet werden können, sollen folgende Beispiele zeigen: Scan, ScanShort, ScanFile und ScannerIterator.

Programme, die den Scanner nutzen, verwenden den Scanner 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.
Scanner quasi ein Iterator
...

s = new Scanner(sourcecode)

do {
        token = s.next()

        tue_was_mit(token)

} while (token != EOS) // hasNext?

...
Wie aus dem Scanner ein richtiger Iterator gemacht werden kann, sehen Sie weiter unten am Beispiel ScannerIterator.
Das Anwendungsbeispiel Scan wandelt den Zeichenstrom eines gegebenen Tinyray-Programms lediglich in einen Tokenstrom um und gibt diesen anschließend auf der Konsole aus.
Scan.java
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 = (+ 1) + ". Token[";
            tokenInfo += "type=" + type + "; ";
            tokenInfo += "location=(" + location.getCharNumberOfLine() + ",";
            tokenInfo += location.getLineNumber() + "); ";
            tokenInfo += "value='" + value + "'";
            tokenInfo += "]";

            System.out.println(tokenInfo);
        }
    }
}
Wird das Anwendungsbeispiel Scan ohne Argumente ausgeführt, so wird default-mäßig das kürzest mögliche Tinyray-Programm
  1. „Tinyray{}”
als Argument verwendet, welches folgende Tokens erkennt.
java Scan
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='']
Wird beispielsweise dem Anwendungsbeispiel Scan das Tinyray-Quellprogramm
  1. „Tinyray{Sphere{[0.3,0,1];2.0}}”
als Argument mitgegeben, so erfolgt folgende Ausgabe.
java Scan "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='']
Ein kürzeres Anwendungsbeispiel als Scan ist ScanShort, welches nicht erst ein Tinyray-Programm in einen Tokenstrom umwandelt, sondern welches gleich jedes erkannte Token auf der Konsole ausgibt.
ScanShort.java
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);
    }
}
ScanShort liefert im Prinzip die selben Ergebnisse wie Scan.
java ScanShort
TINYRAY "Tinyray" in [0:0]
LBRACE "{" in [0:7]
RBRACE "}" in [0:8]
EOS "" in [0:9]
java ScanShort "Tinyray{Sphere{[0.3,0,1];2.0}}"
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]
Ein ebenso kurzes Anwendungsbeispiel wie ScanShort ist ScanFile, nur mit dem kleinen Unterschied, dass das Tinyray-Programm nicht als String, sondern als Datei (FileInputStream) dem Scanner übergeben wird.
ScanFile.java
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);
    }
}
Quellprogramme umfassen eigentlich immer mehr als nur eine Zeile Code wie beispielsweise das „Hallo Welt”-Beispiel
hello-world.tinyray
// 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?
    }
}
… welches nach dem Scan-Vorgang mit ScanFile folgenden Output liefert.
java ScanFile
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]
Da dem Scanner auch Dateien übergeben werden können, können nun auch beliebig lange Tinyray-Quellprogramme gescannt werden.
pyramid.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] }
}
Was dem Leser hier an dieser Stelle verborgen bleibt, ist, dass die Einrückung der Zeilen in der Datei „pyramid.tinyray” mit einem TAB-Zeichen gesetzt sind. Ein TAB-Zeichen wird hier als normales Zeichen gelesen.
java ScanFile pyramid.tinyray
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]
Zugegebenermaßen ist der Output von „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.










Im Folgenden kommt noch das letzte Scanner-Beispiel, welches Ihnen eine Implementierung eines Iterators zeigt, der ScannerIterator. Wenn es nicht zu einer IOException kommen sollte, dann liefert dieses Beispiel den gleichen Output wie das ScanFile-Beispiel. 2
ScannerIterator.java
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;
            }
        }
    }
}