Startseite < Informatik < Algorithmen Datenstrukturen / Software-Engineering / Programmiersprachen < Compiler Interpreter < Setty Tinyray < Tinyray-Scanner Tinyray-Parser / Tinyray-Language < Tinyray-LanguageKit Tinyray-Visitables Tinyray-Visitors < [ DRUCK , 2004 , 2005 , 2006 , 2007 , 2008 , 2009 ] Tinyray-PrettyPrint Tinyray-DotCode Tinyray-PovRayCode Tinyray-GlutCCode Tinyray-VRMLCode > > Tinyray-Raytracer / Tinyray-Wallpapers > > / Java C/C++ POV-Ray LaTeX > / Künstliche Intelligenz > Schach Privates / Inhalt >
Tinyray-PrettyPrint
PrettyPrint erzeugt aus einem Tinyray-Programm wiederum ein Tinyray-Programm, aber sauber formatiert.
PrettyPrint Über PrettyPrint
Zielvorstellung
Ein Tinyray-Quellprogramm soll wiederum in ein Tinyray-Programm übersetzt werden, nur mit dem kleinen Unterschied, dass das resultierende Tinyray-Programm sauber formatiert sein wird.
Im Folgenden soll mit einem inkonsistenten, aber dennoch korrekten Tinyray-Quellprogramm die Leistungsstärke von PrettyPrint demonstriert werden.

Das Tinyray-Programm …
dirty.tinyray
Tinyray {Camera { [1,1,5];[-.13E-000
, 0,0]}Sun{[1,                       1, 0001.000];
// Ein wirklich schlecht formatiertes Programm!
1, 1, 1]}Sphere{    [-1, 0, 0]; 1.0; [
0.8, .50, 0
]}Sphere { [1, 0, 0];
0.6; [.8, 0, 0]}}
… sieht nach der Nutzung von PrettyPrint wie folgt aus.
java UsePrettyPrint dirty.tinyray
Tinyray {
    Camera {
        [1.0, 1.0, 5.0]; // Location
        [-0.13, 0.0, 0.0]; // Look At
        [0.0, 1.0, 0.0] // Up
    }
    Ambience {
        [1.0, 1.0, 1.0] // Color
    }
    Background {
        [0.0, 0.0, 0.0] // Color
    }
    Fog {
        [0.0, 0.0, 0.0]; // Color
        0.0 // Density
    }
    Sun {
        [1.0, 1.0, 1.0]; // Direction
        [1.0, 1.0, 1.0] // Color
    }
    Sphere {
        [-1.0, 0.0, 0.0]; // Center
        1.0; // Radius
        [0.8, 0.5, 0.0] // Diffuse Color
    }
    Sphere {
        [1.0, 0.0, 0.0]; // Center
        0.6; // Radius
        [0.8, 0.0, 0.0] // Diffuse Color
    }
}

// PrettyPrint-Test: OKAY.
Der PrettyPrint-Test
Die letzte Zeile des von PrettyPrint generierten Codes lautet:
  1. // PrettyPrint-Test: OKAY.
Es wird nämlich geprüft, ob
  1. PrettyPrint(code) = PrettyPrint(PrettyPrint(code))
ist.
Implementierung Implementierung der PrettyPrint-Klasse
Die Java-Klasse PrettyPrint ist hier als Visitor realisiert.
PrettyPrint.java
package tinyray.language;

import java.util.Iterator;

/**
 * Die Klasse PrettyPrint implementiert einen Besucher (Visitor),
 * der aus einem Syntaxbaum, der mit Hilfe von LanguageKit erzeugt ist,
 * wieder einen sauber formatierten Tinyray-Code erzeugt.
 */

public class PrettyPrint implements Visitor<String, String> {

    // Der Zeilenumbruch.
    private final static String NEWLINE = "\r\n";

    // Die Zeileneinrückung.
    private final static String INDENT = "\t";

    /**
     * Initialisiert den PrettyPrint.
     */

    public PrettyPrint() {}

    /**
     * Besucht einen besuchbaren Tinyray-Knoten und generiert einen
     * entsprechenden, sauber formatierten "Tinyray"-Code mit einer
     * bestimmten Einrückung und gibt diesen Code als Zeichenkette zurück.
     * @param tinyray Der besuchbare Knoten.
     * @param indent Die Einrückung.
     * @return Der generierte "Tinyray"-Code.
     */

    public String visit(Tinyray tinyray, String indent) {
        String result = "";
        result += indent + "Tinyray {";
        result += NEWLINE;
        result += tinyray.camera.accept(this, indent + INDENT);
        result += NEWLINE;
        result += tinyray.ambience.accept(this, indent + INDENT);
        result += NEWLINE;
        result += tinyray.background.accept(this, indent + INDENT);
        result += NEWLINE;
        result += tinyray.fog.accept(this, indent + INDENT);
        result += NEWLINE;
        Iterator<Sun> suns = tinyray.suns.iterator();
        while (suns.hasNext()) {
            result += suns.next().accept(this, indent + INDENT);
            result += NEWLINE;
        }
        Iterator<Visitable> visitables = tinyray.visitables.iterator();
        while (visitables.hasNext()) {
            result += visitables.next().accept(this, indent + INDENT);
            result += NEWLINE;
        }
        result += indent + "}"; // End of Tinyray
        return result;
    }

    /**
     * Besucht einen besuchbaren Camera-Knoten und generiert einen
     * entsprechenden, sauber formatierten "Tinyray"-Code mit einer
     * bestimmten Einrückung und gibt diesen Code als Zeichenkette zurück.
     * @param camera Der besuchbare Knoten.
     * @param indent Die Einrückung.
     * @return Der generierte "Tinyray"-Code.
     */

    public String visit(Camera camera, String indent) {
        String result = "";
        result += indent + "Camera {";
        result += NEWLINE;
        result += camera.location.accept(this, indent + INDENT);
        result += "; // Location" + NEWLINE;
        result += camera.lookAt.accept(this, indent + INDENT);
        result += "; // Look At" + NEWLINE;
        result += camera.up.accept(this, indent + INDENT);
        result += " // Up" + NEWLINE;
        result += indent + "}";
        return result;
    }

    /**
     * Besucht einen besuchbaren Background-Knoten und generiert einen
     * entsprechenden, sauber formatierten "Tinyray"-Code mit einer
     * bestimmten Einrückung und gibt diesen Code als Zeichenkette zurück.
     * @param background Der besuchbare Knoten.
     * @param indent Die Einrückung.
     * @return Der generierte "Tinyray"-Code.
     */

    public String visit(Background background, String indent) {
        String result = "";
        result += indent + "Background {";
        result += NEWLINE;
        result += background.color.accept(this, indent + INDENT);
        result += " // Color" + NEWLINE;
        result += indent + "}";
        return result;
    }

    /**
     * Besucht einen besuchbaren Ambience-Knoten und generiert einen
     * entsprechenden, sauber formatierten "Tinyray"-Code mit einer
     * bestimmten Einrückung und gibt diesen Code als Zeichenkette zurück.
     * @param ambience Der besuchbare Knoten.
     * @param indent Die Einrückung.
     * @return Der generierte "Tinyray"-Code.
     */

    public String visit(Ambience ambience, String indent) {
        String result = "";
        result += indent + "Ambience {";
        result += NEWLINE;
        result += ambience.color.accept(this, indent + INDENT);
        result += " // Color" + NEWLINE;
        result += indent + "}";
        return result;
    }

    /**
     * Besucht einen besuchbaren Fog-Knoten und generiert einen
     * entsprechenden, sauber formatierten "Tinyray"-Code mit einer
     * bestimmten Einrückung und gibt diesen Code als Zeichenkette zurück.
     * @param fog Der besuchbare Knoten.
     * @param indent Die Einrückung.
     * @return Der generierte "Tinyray"-Code.
     */

    public String visit(Fog fog, String indent) {
        String result = "";
        result += indent + "Fog {";
        result += NEWLINE;
        result += fog.color.accept(this, indent + INDENT);
        result += "; // Color" + NEWLINE;
        result += fog.density.accept(this, indent + INDENT);
        result += " // Density" + NEWLINE;
        result += indent + "}";
        return result;
    }

    /**
     * Besucht einen besuchbaren Defaults-Knoten und generiert einen
     * entsprechenden, sauber formatierten "Tinyray"-Code mit einer
     * bestimmten Einrückung und gibt diesen Code als Zeichenkette zurück.
     * @param defaults Der besuchbare Knoten.
     * @param indent Die Einrückung.
     * @return Der generierte "Tinyray"-Code.
     */

    public String visit(Defaults defaults, String indent) {
        String result = "";
        result += indent + "Defaults {";
        result += NEWLINE;
        result += defaults.parameters.accept(this, indent + INDENT);
        result += NEWLINE;
        result += indent + "}";
        return result;
    }

    /**
     * Besucht einen besuchbaren Sun-Knoten und generiert einen
     * entsprechenden, sauber formatierten "Tinyray"-Code mit einer
     * bestimmten Einrückung und gibt diesen Code als Zeichenkette zurück.
     * @param sun Der besuchbare Knoten.
     * @param indent Die Einrückung.
     * @return Der generierte "Tinyray"-Code.
     */

    public String visit(Sun sun, String indent) {
        String result = "";
        result += indent + "Sun {";
        result += NEWLINE;
        result += sun.direction.accept(this, indent + INDENT);
        result += "; // Direction" + NEWLINE;
        result += sun.color.accept(this, indent + INDENT);
        result += " // Color" + NEWLINE;
        result += indent + "}";
        return result;
    }

    /**
     * Besucht einen besuchbaren Bounding-Knoten und generiert einen
     * entsprechenden, sauber formatierten "Tinyray"-Code mit einer
     * bestimmten Einrückung und gibt diesen Code als Zeichenkette zurück.
     * @param bounding Der besuchbare Knoten.
     * @param indent Die Einrückung.
     * @return Der generierte "Tinyray"-Code.
     */

    public String visit(Bounding bounding, String indent) {
        String result = "";
        result += indent + "Bounding {";
        result += NEWLINE;
        Iterator<Visitable> visitables = bounding.visitables.iterator();
        while (visitables.hasNext()) {
            result += visitables.next().accept(this, indent + INDENT);
            result += NEWLINE;
        }
        result += indent + "}";
        return result;
    }

    /**
     * Besucht einen besuchbaren Triangle-Knoten und generiert einen
     * entsprechenden, sauber formatierten "Tinyray"-Code mit einer
     * bestimmten Einrückung und gibt diesen Code als Zeichenkette zurück.
     * @param triangle Der besuchbare Knoten.
     * @param indent Die Einrückung.
     * @return Der generierte "Tinyray"-Code.
     */

    public String visit(Triangle triangle, String indent) {
        String result = "";
        result += indent + "Triangle {";
        result += NEWLINE;
        result += triangle.x.accept(this, indent + INDENT);
        result += "; // X" + NEWLINE;
        result += triangle.y.accept(this, indent + INDENT);
        result += "; // Y" + NEWLINE;
        result += triangle.z.accept(this, indent + INDENT);
        if (triangle.parameters.visitables.size() > 0) {
            result += "; // Z" + NEWLINE;
            result += triangle.parameters.accept(this, indent + INDENT);
            result += NEWLINE;
        } else {
            result += " // Z" + NEWLINE;
        }
        result += indent + "}";
        return result;
    }

    /**
     * Besucht einen besuchbaren Sphere-Knoten und generiert einen
     * entsprechenden, sauber formatierten "Tinyray"-Code mit einer
     * bestimmten Einrückung und gibt diesen Code als Zeichenkette zurück.
     * @param sphere Der besuchbare Knoten.
     * @param indent Die Einrückung.
     * @return Der generierte "Tinyray"-Code.
     */

    public String visit(Sphere sphere, String indent) {
        String result = "";
        result += indent + "Sphere {";
        result += NEWLINE;
        result += sphere.center.accept(this, indent + INDENT);
        result += "; // Center" + NEWLINE;
        result += sphere.radius.accept(this, indent + INDENT);
        if (sphere.parameters.visitables.size() > 0) {
            result += "; // Radius" + NEWLINE;
            result += sphere.parameters.accept(this, indent + INDENT);
            result += NEWLINE;
        } else {
            result += " // Radius" + NEWLINE;
        }
        result += indent + "}";
        return result;
    }

    /**
     * Besucht einen besuchbaren Plane-Knoten und generiert einen
     * entsprechenden, sauber formatierten "Tinyray"-Code mit einer
     * bestimmten Einrückung und gibt diesen Code als Zeichenkette zurück.
     * @param plane Der besuchbare Knoten.
     * @param indent Die Einrückung.
     * @return Der generierte "Tinyray"-Code.
     */

    public String visit(Plane plane, String indent) {
        String result = "";
        result += indent + "Plane {";
        result += NEWLINE;
        result += plane.x.accept(this, indent + INDENT);
        result += "; // X" + NEWLINE;
        result += plane.y.accept(this, indent + INDENT);
        result += "; // Y" + NEWLINE;
        result += plane.z.accept(this, indent + INDENT);
        if (plane.parameters.visitables.size() > 0) {
            result += "; // Z" + NEWLINE;
            result += plane.parameters.accept(this, indent + INDENT);
            result += NEWLINE;
        } else {
            result += " // Z" + NEWLINE;
        }
        result += indent + "}";
        return result;
    }

    /**
     * Besucht einen besuchbaren Parameters-Knoten und generiert einen
     * entsprechenden, sauber formatierten "Tinyray"-Code mit einer
     * bestimmten Einrückung und gibt diesen Code als Zeichenkette zurück.
     * @param parameters Der besuchbare Knoten.
     * @param indent Die Einrückung.
     * @return Der generierte "Tinyray"-Code.
     */

    public String visit(Parameters parameters, String indent) {
        String result = "";

        if (parameters.visitables.size() > 0) {
            result += parameters.visitables.get(0).accept(this, indent);
            if (parameters.visitables.size() > 1) {
                result += ";";
            }
            result += " // Diffuse Color";
        }
        if (parameters.visitables.size() > 1) {
            result += NEWLINE;
            result += parameters.visitables.get(1).accept(this, indent);
            if (parameters.visitables.size() > 2) {
                result += ";";
            }
            result += " // Ambient Color";
        }
        if (parameters.visitables.size() > 2) {
            result += NEWLINE;
            result += parameters.visitables.get(2).accept(this, indent);
            if (parameters.visitables.size() > 3) {
                result += ";";
            }
            result += " // Specular";
        }
        if (parameters.visitables.size() > 3) {
            result += NEWLINE;
            result += parameters.visitables.get(3).accept(this, indent);
            if (parameters.visitables.size() > 4) {
                result += ";";
            }
            result += " // Shininess";
        }
        if (parameters.visitables.size() > 4) {
            result += NEWLINE;
            result += parameters.visitables.get(4).accept(this, indent);
            result += " // Mirror";
        }
        return result;
    }

    /**
     * Besucht einen besuchbaren Vector-Knoten und generiert einen
     * entsprechenden, sauber formatierten "Tinyray"-Code mit einer
     * bestimmten Einrückung und gibt diesen Code als Zeichenkette zurück.
     * @param vector Der besuchbare Knoten.
     * @param indent Die Einrückung.
     * @return Der generierte "Tinyray"-Code.
     */

    public String visit(Vector vector, String indent) {
        String result = "";
        result += indent + "[";
        result += vector.x.accept(this, "");
        result += ", ";
        result += vector.y.accept(this, "");
        result += ", ";
        result += vector.z.accept(this, "");
        result += "]";
        return result;
    }

    /**
     * Besucht einen besuchbaren Real-Knoten und generiert einen
     * entsprechenden, sauber formatierten "Tinyray"-Code mit einer
     * bestimmten Einrückung und gibt diesen Code als Zeichenkette zurück.
     * @param real Der besuchbare Knoten.
     * @param indent Die Einrückung.
     * @return Der generierte "Tinyray"-Code.
     */

    public String visit(Real real, String indent) {
        String result = "";
        result += indent + real.value;
        return result;
    }
}
Anwendung Anwendung der PrettyPrint-Klasse
Folgende Anwendung nutzt die Klasse PrettyPrint und wandelt ein gegebenes Tinyray-Programm in ein sauber formatiertes Tinyray-Programm um.
UsePrettyPrint.java
package tinyray;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

import tinyray.frontend.ParseException;
import tinyray.frontend.Parser;
import tinyray.frontend.Scanner;
import tinyray.language.LanguageKit;
import tinyray.language.PrettyPrint;
import tinyray.language.Tinyray;
import tinyray.language.Visitable;

/**
 * Die Klasse UsePrettyPrint wandelt einen Tinyray-Code in einen sauber
 * formatierten Tinyray-Code um.
 */

public class UsePrettyPrint {

    /**
     * Das Hauptprogramm, welches einen gegebenen Tinyray-Code in einen
     * sauber formatierten Tinyray-Code umwandelt.
     * Zudem wird der PrettyPrint-Test angewendet und ausgegeben.
     * Das Ergebnis wird auf der Konsole ausgegeben (System.out).
     * @param arguments Die Aufrufargumente.
     *        Das erste Argument gibt den Dateinamen der Tinyray-Datei an.
     *        Ist das erste Argument nicht gesetzt, erhält der Benutzer
     *        einen kurzen Hilfetext.
     */

    public static void main(String[] arguments) {

        // Mindestens ein Argument erforderlich: <Dateiname>
        if (arguments.length > 0) {

            try {
                // Der Scanner erhält den Tinyray-Code als Stream.
                Scanner scanner =
                    new Scanner(new FileInputStream(arguments[0]));

                // Der Parser erhält den Scanner und eine konkreten Fabrik
                // (hier: LanguageKit).
                Parser<Visitable, Tinyray> parser =
                    new Parser<Visitable, Tinyray>(scanner, new LanguageKit());

                // Der Parse-Vorgang bringt einen Syntaxbaum hervor.
                // Die einzelnen Knoten des Syntaxbaumes werden mit Hilfe
                // der konkreten Fabrik "LanguageKit", die dem Parser bei
                // der Instantiierung mitgegeben wird, erzeugt.
                Tinyray tinyray = parser.parse();

                // Der Syntaxbaum besteht aus besuchbaren Knoten (accept).
                // PrettyPrint ist ein Besucher, der die Wurzel des
                // Syntaxbaums besucht und dabei einen sauber formatierten
                // Tinyray-Code (prettyprint) generiert.
                String prettyprint = tinyray.accept(new PrettyPrint(), "");

                // Ausgabe des sauber formatierten Tinyray-Codes.
                System.out.println(prettyprint);

                // PrettyPrint-Test
                System.out.print("\r\n// PrettyPrint-Test: ");
                FileInputStream input = new FileInputStream(arguments[0]);

                if (UsePrettyPrint.isValid(input)) {
                    System.out.println("OKAY.");
                } else {
                    // Wird der Else-Zweig besucht, arbeitet der Parser
                    // oder der PrettyPrinter inkorrekt, d.h.
                    // PrettyPrint(code) != PrettyPrint(PrettyPrint(code))
                    // mit der Voraussetzung code steht für ein korrektes
                    // Tinyray-Quellprogramm.
                    System.out.println("FAILED.");
                }

            } catch (ParseException e) {
                // Fehlermeldung, falls gegebener Tinyray-Code fehlerhaft.
                System.err.println(e.getMessage());
            } catch (FileNotFoundException e) {
                // Fehlermeldung, falls das erste Argument keine Datei benennt.
                System.err.println(e.getMessage());
            }

        } else {
            // Kurzhilfe, falls Aufruf ohne Argument.
            System.out.println(usage());
        }
    }

    /**
     * Erläuterung zum korrekten Aufruf (Kurzhilfe).
     * @return Die Kurzhilfe als Zeichenkette.
     */

    public static String usage() {
        String result = "";
        result += "java tinyray/UsePrettyPrint <TinyrayDatei>\r\n";
        result += "\r\nBeispiele:\r\n";
        result += " java tinyray/UsePrettyPrint MyScene.tinyray\r\n";
        result += " java tinyray/UsePrettyPrint Test01.txt\r\n";
        result += "\r\nHinweis:\r\n";
        result += " Mindestens JRE (java-1.6.0) erforderlich.\r\n";
        return result;
    }

    /**
     * Prüft, ob der Parser bezüglich eines bestimmten Eingabestroms
     * (Tinyray-Programm) korrekt arbeitet. Dies wird geprüft mit der Formel:
     * PrettyPrint(input) = PrettyPrint(PrettyPrint(input)).
     * @param input Der Eingabestrom.
     * @return true, falls korrekt.
     * @throws ParseException Der Eingabestrom muss fehlerfrei geparst
     *         werden können, falls nicht wird eine ParseException geworfen.
     *         Wenn eine ParseException auftritt, kann die Korrektheit
     *         bzw. die Inkorrektheit des Parsers nicht nachgewiesen werden.
     */

    public static boolean isValid(InputStream input) throws ParseException {

        // PrettyPrint aus Eingabestrom erzeugen.
        Scanner scanner1 = new Scanner(input);
        Parser<Visitable, Tinyray> parser1 =
            new Parser<Visitable, Tinyray>(scanner1, new LanguageKit());
        Tinyray tinyray1 = parser1.parse();
        String prettyprint1 = tinyray1.accept(new PrettyPrint(), "");

        // PrettyPrint aus PrettyPrint des Eingabestroms erzeugen.
        Scanner scanner2 = new Scanner(prettyprint1);
        Parser<Visitable, Tinyray> parser2 =
            new Parser<Visitable, Tinyray>(scanner2, new LanguageKit());
        Tinyray tinyray2 = parser2.parse();
        String prettyprint2 = tinyray2.accept(new PrettyPrint(), "");

        // Prüfen, ob das erste PrettyPrint exakt dem zweiten PrettyPrint
        // gleicht.
        return prettyprint1.equals(prettyprint2);
    }
}
Ein Programmaufruf ohne Aufrufparameter zeigt dem Aufrufenden die Programmkurzhilfe (usage).
java UsePrettyPrint
java tinyray/UsePrettyPrint <TinyrayDatei>

Beispiele:
 java tinyray/UsePrettyPrint MyScene.tinyray
 java tinyray/UsePrettyPrint Test01.txt

Hinweis:
 Mindestens JRE (java-1.5.0) erforderlich.