Quelltextähnlichkeit

Ähnliche Dokumente
Quelltextähnlichkeit

Einführung in die Programmiertechnik

Programmierung 2. Übersetzer: Das Frontend. Sebastian Hack. Klaas Boesche. Sommersemester

Programme erstellen in Java

Definition Compiler. Bekannte Compiler

Java Lexikalische Struktur

Kapitel 1: Informationsverarbeitung durch Programme

Grammatiken und ANTLR

Das diesem Dokument zugrundeliegende Vorhaben wurde mit Mitteln des Bundesministeriums für Bildung und Forschung unter dem Förderkennzeichen

Beuth Hochschule Einführende Gentle-Programme WS12/13, S. 1

Wintersemester Maschinenbau und Kunststofftechnik. Informatik. Tobias Wolf Seite 1 von 29

1. Der Begriff Informatik 2. Syntax und Semantik von Programmiersprachen - 1 -

1. Der Begriff Informatik 2. Syntax und Semantik von Programmiersprachen - 1 -

Einstieg in die Informatik mit Java

Interpreter - Gliederung

Einführung in die Informatik: Programmierung und Software-Entwicklung, WS 12/13. Kapitel 3. Grunddatentypen, Ausdrücke und Variable

Einstieg in die Informatik mit Java

Übungs- und Praktikumsaufgaben zur Systemprogrammierung Dipl.-Ing. H. Büchter (Lehrbeauftragter) FH-Dortmund WS 2001/2002 / SS 2002

Methoden. Gerd Bohlender. Einstieg in die Informatik mit Java, Vorlesung vom

Definition von domänenspezifischen Sprachen mit Xtext: Einführung

Inhalt Kapitel 11: Formale Syntax und Semantik

Objektorientierte Programmierung. Kapitel 3: Syntaxdiagramme

Literatur Reguläre Ausdrücke

n 1. Der Begriff Informatik n 2. Syntax und Semantik von Programmiersprachen - 1 -

Kapitel 1: Informationsverarbeitung durch Programme

Programmieren I + II Regeln der Code-Formatierung

Einführung in die C-Programmierung

pue13 January 28, 2017

7. Syntax: Grammatiken, EBNF

Grundlagen der Informatik Vorlesungsskript

Programmierkurs II. C und Assembler

Softwareentwicklung I (IB) Objekte. Prof. Dr. Oliver Braun. Fakultät für Informatik und Mathematik Hochschule München

Mapra: C++ Teil 3. Felix Gruber. 29. April IGPM, RWTH Aachen. Felix Gruber (IGPM, RWTH Aachen) Mapra: C++ Teil 3 29.

Programmierpraktikum Java Entdecken Merkblatt 2

Methoden zur Interpretation LISPähnlicher. Programmiersprachen. Seminarvortrag / 53 FH AACHEN FACHBEREICH 9 TOBIAS STUMM MATR.-NR.

Java-Applikationen (Java-Programme)

Einstieg in die Informatik mit Java

Java Übung. Übung 2. Werner Gaulke. 19. April Universität Duisburg-Essen Kommedia, Übung EinPro SS06, Einführung in Java - Übung.

Kapitel 2. Methoden zur Beschreibung von Syntax

3 Syntax von Programmiersprachen

2 Eine einfache Programmiersprache

Methoden und Wrapperklassen

Implementierung eines LR-Parser-Generators mit syntaktischen Prädikaten

Einstieg in die Informatik mit Java

C++ Teil 2. Sven Groß. 16. Apr IGPM, RWTH Aachen. Sven Groß (IGPM, RWTH Aachen) C++ Teil Apr / 22

Ergänzungen zur Theoretischen Informatik. Compilerbau mit ANTLR. Literatur Das Standardwerk zum Compilerbau ist [1].

Java I Vorlesung Imperatives Programmieren

Einführung in die Informatik I (autip)

FACHHOCHSCHULE MANNHEIM

Programmieren I + II Regeln der Code-Formatierung

C - PRÄPROZESSOR. Seminar effiziente C Programmierung WS 2012/13. Von Christian Peter

FACHHOCHSCHULE AUGSBURG Hochschule für Technik, Wirtschaft und Gestaltung

Vorlesung Software-Reengineering

Programmiersprachen und Übersetzer

Einführung in die Computerlinguistik Einführung in Perl (1)

Kapitel 05. Datentypen. Fachgebiet Knowledge Engineering Prof. Dr. Johannes Fürnkranz

Werkzeuge zur Programmentwicklung

Repetitorium Informatik (Java)

Kapitel 3. Grunddatentypen, Ausdrücke und Variable

Formale Sprachen. Inhaltsverzeichnis. M. Jakob. 10. Dezember Allgemeine Einführung. Aufbau formaler Sprachen

Übung zur Vorlesung Wissenschaftliches Rechnen Sommersemester 2012 Auffrischung zur Programmierung in C++, 1. Teil

Kapitel 9. Programmierkurs. Attribute von Klassen, Methoden und Variablen. 9.1 Attribute von Klassen, Methoden und Variablen

Programmierkurs II. Prof. Dr. Wolfgang Effelsberg. Universität Mannheim. Sommersemester Wolfgang Effelsberg Programmiersprachen

Programmierkurs II. Prof. Dr. Wolfgang Effelsberg. Universität Mannheim. Sommersemester Wolfgang Effelsberg Programmiersprachen

Compiler; Übersetzungsprogramme. Grundlagen der Programmierung 3 A. Compiler für Programmiersprachen. Phasen eines Compilers

Verhalten. Def. und Nutzen von Verhalten. Pseudocode Schreibtischtest. Algorithmen

Sprachkonstrukte. Einführung in Java. Folie 1 von Mai Ivo Kronenberg

FHZ. K13 Rekursion. Lernziele. Hochschule Technik+Architektur Luzern Abteilung Informatik, Fach Programmieren. Inhalt

Einstieg in die Informatik mit Java

Beispiel. Problem: mehrteilige Nachnamen (von Goethe, Mac Donald, Di Caprio)

Informatik II Übung 06. Benjamin Hepp 5 April 2017

Einstieg in die Informatik mit Java

Programmierkonventionen - 1 -

Sprachen sind durch folgenden Aufbau gekennzeichnet:

Wo sind wir? Übersicht lexikale Struktur von Java

Einstieg in die Informatik mit Java

ÜBUNGS-BLOCK 1 LÖSUNGEN

Lexikalische Analyse, Tokenizer, Scanner

12 Abstrakte Klassen, finale Klassen und Interfaces

2 Eine einfache Programmiersprache

C# - Einführung in die Programmiersprache Methoden. Leibniz Universität IT Services

Algorithmen implementieren. Implementieren von Algorithmen

Prof. Dr. Uwe Schmidt. 12. August Aufgaben zur Klausur Softwaredesign im SS 2014 (BInf v310, BMInf v300, BWInf v310, BWInf- 23)

Schöner Programmieren

{P} S {Q} {P} S {Q} {P} S {Q} Inhalt. Hoare-Kalkül. Hoare-Kalkül. Hoare-Tripel. Hoare-Tripel. Hoare-Tripel

Schöner Programmieren

Java: Eine kurze Einführung an Beispielen

Programmieren in Java

9.4 Grundlagen des Compilerbaus

Syntax von Programmiersprachen

Test-Klausuraufgaben Softwaretechnik Fachbereich BW, für WINFO

Kapitel 5: Syntaxdiagramme und Grammatikregeln

Transkript:

Quelltextähnlichkeit Tobias Jamin Universität Bremen Fachbereich 3 Mathematik und Informatik tjamin@tzi.de Abstrakt Programm-Transformationen ändern den Quelltext eines Programms. Bei den Transformationen sollen nur die Stellen im Quelltext verändert werden, die von den Transformationen betroffen sind. Der restliche Quelltext soll nicht verändert werden. Diese Eigenschaft wird als Quelltextähnlichkeit bezeichnet. Warum ist Quelltextähnlichkeit überhaupt so wichtig und ist es schwer, eine hohe Quelltextähnlichkeit zu erzielen? Dieser Artikel gibt Antworten auf diese Fragen und beschäftigt sich mit der Problematik, die mit der Quelltextähnlichkeit verbunden ist. Es werden Lösungsansätze vorgestellt, mit denen das Projekt Arte-Fakt versuchen wird, eine möglichst hohe Quelltextähnlichkeit zu erzielen. 1. Sind Syntax und Semantik alles? Die dokumentarische Struktur von Quelltext Ein Programm muss den syntaktischen Ansprüchen der zugrunde gelegten Programmiersprache genügen, damit es sich kompilieren lässt. Neben der Syntax muss auch die Semantik stimmen. Was genau sind aber Syntax und Semantik und sind sie überhaupt für Quelltextähnlichkeit wichtig? 1.1. Syntax & Semantik Syntax ist ein Begriff aus der Linguistik, also der Sprachwissenschaft. Sie gibt mit Regeln an, wie Wörter zu größeren Einheiten, wie Phrasen (Teilsätzen) und dann Sätzen, zusammengesetzt werden. In der Informatik wird eine Sprache durch eine Grammatik (mit Hilfe von terminalen und nicht-terminalen Symbolen) definiert. Beispiele sind die BNF und verschiedene Transformations-Anwendungen, die zum Teil eigene Sprachen zum definieren von Grammatiken benutzen (Beispiel TXL). Auch der Begriff der Semantik kommt aus der Linguistik. Die Semantik ist das Teilgebiet, das sich mit dem Sinn und der Bedeutung von Sprache beschäftigt. Die formale Semantik ist ein Teilgebiet der theoretischen Informatik, die sich mit der Korrektheit von Computerprogrammen beschäftigt. Damit sich Quelltext zu einem ausführbaren Programm kompilieren lässt, muss es syntaktisch korrekt sein, also der Grammatik der gewählten Programmiersprache genügen. Semantische Fehler machen sich dagegen erst zur Laufzeit durch ungewolltes Verhalten oder Abstürze der Anwendung bemerkbar. Ein ungültiger Cast lässt sich beispielsweise kompilieren, führt beim Ausführen allerdings zu einem Laufzeitfehler. Die Bedeutung von Syntax und Semantik bekommt jeder Informatik-Student schon im Grundstudium vermittelt. Diese beiden Begriffe sind allerdings im Zusammenhang mit der Thematik Quelltextähnlichkeit nicht besonders ausschlaggebend. Erst die dokumentarische Struktur von Quelltext gibt der Quelltextähnlichkeit ihre hohe Bedeutung. 1.2. Dokumentarische Struktur Unter die dokumentarische Struktur fallen alle Eigenschaften von Quelltext, die das Erfassen und Verstehen von diesem beeinflussen. Einen großen inhaltlichen Beitrag liefern dazu Zeilensowie Blockkommentare (Abbildung 1).

// Zeilenkommentar /* Blockkommentar (über mehrere Zeilen) Abbildung 1: Kommentare Neben den Kommentaren entsteht der weitaus größere Teil der dokumentarischen Struktur durch die Nutzung von Leerzeichen, Tabs und Zeilenumbrüchen. Kurz gesagt ist dieser Teil der dokumentarischen Struktur das Arrangement von Sprachelementen der jeweiligen Programmiersprache, also das Layout des Quelltextes. Mit Arrangement ist beispielsweise das Einrücken eines Anweisungsblocks in eine bestimmte Tiefe gemeint. Zum Gestalten von Quelltext werden so genannte white spaces benutzt. Unter dem Begriff white spaces werden folgende Elemente zusammengefasst: Zeilenumbrüche Leerzeichen Tabulatoren White spaces sind im Allgemeinen keine Elemente der Grammatik von Programmiersprachen. Dazu muss erwähnt werden, dass es Ausnahmen gibt, wie z. B. die Programmiersprache whitespace [2], welche in diesem Artikel jedoch ignoriert werden. Neben dem Layout des Quelltexts und den Kommentaren zählt die Benennung von Elementen (wie Klassen, Methoden, Variablen usw.) ebenfalls zur dokumentarischen Struktur. Durch bündiges Ausrichten von Datentyp, Variablenbezeichner und zugewiesenen Werten in derselben Spalte kann die Leserlichkeit des Quelltextes, durch den gezielten Einsatz von Leerzeichen, deutlich erhöht werden (Abbildung 2). int a = 5; int bcd = 6; String efg = hallo welt ; Abbildung 2: Beispiel Leerzeichen/Tabulatoren (1) Neben der Leserlichkeit verbessert der gezielte Einsatz von Leerzeichen ebenfalls die dokumentarische Struktur (Abbildung 3). if (condition) expression; else expression; Abbildung 3: Beispiel Leerzeichen/Tabulatoren (2) Durch Zeilenumbrüche werden nicht nur überlange Zeilen vermieden. Die Leserlichkeit kann erhöht werden ebenso wie, z. B. durch den Einsatz von Zeilen- Kommentaren hinter jedem Parameter, die dokumentarische Struktur verbessert werden kann. Object.Method(ParameterWithLongIdentifier1, ParameterWithLongIdentifier2, ParameterWithLongIdentifier3); Abbildung 4: Beispiel Zeilenumbrüche (1) Die Bedeutung von Leerzeilen kann gar nicht überbewertet werden [2]. Das Beispiel in Abbildung 5 zeigt, wie die Zusammengehörigkeit von Anweisungen durch das Vermeiden von Zeilenumbrüchen dargestellt werden soll. Der Quelltext ist jedoch nicht besonders leserlich. int a = 2 int b = 3 int temp=a; a=b; b=temp; if (a Abbildung 5: Beispiel Zeilenumbrüche (2a) Das modifizierte Beispiel in Abbildung 6 zeigt, wie die Zusammengehörigkeit von Anweisungen durch den Einsatz von Leerzeilen deutlich gemacht werden kann. int a = 2 int b = 3 int temp=a; a=b; b=temp; if (a Abbildung 6: Beispiel Zeilenumbrüche (2b) 2. Kommentar-Problematik Kommentare bereiten bei der Transformation von Quelltext große Probleme. Der Grund dafür ist, dass Kommentare im Allgemeinen keine Bestandteile der Grammatik der Programmiersprachen sind. Der Lexer bzw. der Präprozessor entfernt alle Kommentare, bevor der Quelltext zum Parser durchgereicht wird, welcher die syntaktische Überprüfung durchführt. Aus diesem Grund werden Kommentare auch zu den white spaces gezählt Durch dieses Vorgehen ist es nicht für den

Compiler nötig, das Konstrukt Kommentar in die Grammatik aufzunehmen. Für Transformations- Anwendungen wäre es eine enorme Erleichterung, da der Umgang mit Kommenten erleichtert bzw. ermöglicht werden würde. Wären Kommentare Bestandteile der Grammatik, wäre es ohne Probleme möglich, die Kommentare bestimmten Zeilen oder Konstrukten zuzuordnen. An dem Quelltextbeispiel in Abbildung 7 erkennt man recht intuitiv, welcher Kommentar zu welchem Quelltext-Block gehört. Das automatisierte Ermitteln der Zuordnungen ist jedoch nicht so einfach möglich. Wie könnte eine automatische Zuordnung vorgehen? Der erste Kommentar completely before steht vor einer langen If-Else-If Kaskade. Beschreibt der Kommentar also den kompletten Anweisungsblock? Am Inhalt im Zusammenhang mit der ersten Bedingung können wir erkennen, dass der Kommentar nur zu dem ersten Teil des langen Abschnittes gehört. Wenn wir uns die weiteren Kommentare und ihre Positionen aus einem rein syntaktischen Blickwinkel anschauen, scheinen die Kommentare in die jeweils vorhergehenden Anweisungsblöcke zu gehören, da sie sich innerhalb der diese Blöcke umschließenden geschweiften Klammern befinden. Der letzte Kommentar kann überhaupt keiner Quelltext-Zeile zugeordnet werden. // completely before if (delend < start) { // overlap start } else if (pos <= start && delend < end) { // completely overlaps } else if (pos <= start && delend >= end) { // completely after } else { // do nothing } Abbildung 7: Intuitive Zuordnung von Kommentaren Weiter Probleme ergeben sich bei der Nutzung von Zeilenkommentaren als Blockkommentare. Das Beispiel in Abbildung 8 zeigt, wie über mehrere Zeilen mit einzelnen Zeilenkommentaren ein (inhaltlicher) Blockkommentar entsteht. Ein automatisches Ermitteln der Zusammengehörigkeit ist nicht möglich, da der Inhalt der Kommentare, also menschliche Sprache, analysiert werden müsste. Abbildung 8: Blockkommentar vs. Zeilenkommentar (1) In Abbildung 9 ist es ähnlich wie in Abbildung 8. Statt die einleitenden und abschließenden Zeichen für Blockkommentare zu benutzen, entsteht ein Blockkommentar aus Zeilenkommentaren. // Dies ist ein Beispiel mit einem // Blockkommentar aus // zusammengesetzten Zeilenkommentaren Abbildung 9: Blockkommentar vs. Zeilenkommentar (2) Was ist mit dem nächsten Beispiel in Abbildung 10? Handelt es sich hier um einen zusammenhängenden Kommentarblock oder um zwei einzelne Kommentare? Wieder erkennt man nur am Inhalt, dass es sich eigentlich um zwei getrennte Kommentare handelt. // Berechnung X abgeschlossen // // Nun beginnt Berechnung Y Abbildung 10: Blockkommentar vs. Zeilenkommentar (3) Die Verwendung von white spaces macht in Abbildung 11 und Abbildung 12 deutlich, wie groß der Einfluss der dokumentarischen Struktur auf die Wahrnehmung von Quelltextes ist. Method(parameter1, /* K1 */ parameter2, /* K2 */ parameter3) /* K3 */ Abbildung 11: Dokumentarische Struktur unterstützt Wahrnehmung der Zugehörigekeit von Kommentaren In Abbildung 11 erkennt man genau, dass die Kommentare den einzelnen Parametern zuzuordnen sind, da die Interpretation instinktiv zeilenweise geschieht. In Abbildung 12 ist eine zeilenweise Interpretation nicht möglich. Durch die syntaktische Interpretation scheinen die Kommentare K1 und K2 zu den auf sie folgenden Parametern zu gehören. Kommentar K3 scheint die Methode zu kommentieren. if (a < b) // Zusammenhängender return a; // Kommentar

Method(parameter1, /* K1 */ parameter2, /* K2 */ parameter3) /* K3 */ Abbildung 12: Fehlende dokumentarische Struktur Quelltext (mit Makros) Macros, Kommentare Präprozessor Quelltext white spaces, Kommentare Lexer An diesen Beispielen wird sehr schnell deutlich, dass es oft nicht möglich ist, Kommentare bestimmten Quelltext-Abschnitten eindeutig zuzuordnen. semantische Analyse Parse-Baum Parser Tokenstrom AST 3. Quelltext-Zwischendarstellung Um effektiv auf Quelltext arbeiten zu können, wird dieser in eine Zwischendarstellung überführt, auf der Operationen effizienter möglich sind, als auf reinem Text. Solche sprachbasierten Anwendungen arbeiten auf der sprachlichen Struktur, der Grammatik, der Programmiersprache. Die konventionelle Datenstruktur für die Repräsentation eines Programms in Form ihrer Grammatik stammt aus dem Compiler-Bau und ist ein Parse-Baum. 3.1. Parse-Bäume Die Bäume, auf denen gearbeitet wird, sind von der Grammatik der jeweiligen Programmiersprache abgeleitet. Genau dies ist der Grund, aus dem es beim Unparsen Probleme mit der dokumentarischen Struktur gibt. Diese ist nämlich gerade nicht in der Grammatik festgeschrieben. Um einen Parse-Baum aufzubauen werden dazu zunächst alle Quelltextdateien von einem Lexer eingelesen, welcher sie in einen Tokenstrom umwandelt. Handelt es sich bei den eingelesenen Quelltextdateien um C oder C++ Code, so wird der Quelltext vor dem Lexer noch durch einen so genannten Präprozessor vorverarbeitet. Läuft der Quelltext vor dem Lexer durch einen Präprozessor, so löscht dieser die Kommentare. Ansonsten werden die Kommentare vom Lexer beim Erzeugen des Tokenstroms verworfen. Der Tokenstrom wird an einen Parser weitergereicht der anhand der Grammatik den Parse-Baum aufbaut. Wird der Parse- Baum um weitere Informationen, wie beispielsweise semantische Kanten, erweitert, so spricht man von einem AST, einem Abstrakten Syntax Baum (abstract syntax tree). Da für white spaces keine Token erzeugt werden, geht die dokumentarische Struktur schon beim Lexen verloren. Abbildung 13: Erzeugung eines ASTs 3.2. Probleme mit Parse-Bäumen und ASTs Die Vorgehensweise, Quelltext in Parse-Bäumen bzw. ASTs zu verwalten, wurde für Compiler entwickelt [1]. Die Problematik, die sich hierbei für die Quelltextähnlichkeit ergibt, zeigt schon die Bezeichnung abstrakter Syntaxbaum auf. Lexer und Parser bilden nur die Grammatik der Programmiersprach ab und abstrahieren dabei so stark vom Quelltext, dass alle Bestandteile, die sich zur dokumentarischen Struktur zählen lassen, verloren gehen. Alle white spaces gehen beim Umwandeln von Quelltext in den Tokenstrom durch den Lexer verloren. Da white spaces den Parser nicht mehr erreichen, können sie an dieser Stelle nicht mehr in den AST eingearbeitet werden. Gerade diese white spaces sind elementare Bestandteile der dokumentarischen Struktur von Quelltext. Liegt beim Unparsen allein der abstrakte Syntaxbaum vor, ist eine quelltextnahe Ausgabe aufgrund der vielen fehlenden Informationen nicht möglich. Mit reinen abstrakten Syntaxbäumen ist lediglich Typ 3 der Quelltextähnlichkeit erreichbar (Vorgriff auf 4. Definition von Quelltextähnlichkeit auf Seite 5). 3.3. Beispiele aus der IML (Bauhaus) Die Zwischendarstellung des Werkzeugkastens Bauhaus, die IML, hat zurzeit noch einige Schwächen, die zu Problemen mit einem möglichst quelltextähnlich Unparse führen. Die folgende Liste führt drei Beispiele von Elementen auf, die nicht in der IML repräsentiert werden und somit beim Unparse nicht korrekt wiedergegeben werden können.

optionale Leerzeichen (Abbildung 14) Zeilenumbrüche redundante Klammern (Abbildung 15) public void Method(int a, int b) Abbildung 14: optionale Leerzeichen int a = b + (c * d); Abbildung 15: redundante Klammern Ein weiteres Problem gibt es bei der Unterstützung des Zeichensatzes der verschiedenen Programmiersprachen. Zurzeit kann die IML nur ASCII Zeichen darstellen. Java unterstützt jedoch Unicode. Bezeichner die Unicode-Zeichen nutzen, können in der IML also zurzeit nicht dargestellt werden. 4. Definition von Quelltextähnlichkeit R. Falke beschreibt in seiner Ausarbeitung Ergebnisse des Projektwochenendes in Nordenham und weitere Notizen zu Arte-Fakt [4] die folgenden drei Typen (Ausbau-Stufen) von Quelltextähnlichkeit für das Projekt Arte-Fakt: Typ 0 Binäre Gleichheit der Quelltext-Dateien (bezogen auf die nicht von Transformationen betroffenen Quelltextfragmente. Typ 1 Wie Typ 0 aber zusätzlich ist erlaubt, dass sich der Typ des Leerraumes ändert (Tab -> Leerzeichen, Leerzeichen -> Tab). Die Position der Elemente muss gleich bleiben. Zusätzlich gehen eventuelle Leerzeichen an Zeilenenden verloren. Typ 2 Wie Typ 1. Zusätzlich darf sich die Position der E- lemente verändern. 5. Lösungsansätze der Gruppe Unparse Um durch die IML verloren gegangene Informationen zu bekommen, soll ein erweiterter Tokenstrom verwendet werden, der beispielsweise spezielle Token für Leerzeichen und Tabulatoren enthält. Durch die Verwendung eines Tokenstroms und der in jedem Token enthaltenen Source-Location (SLoc) können Zeilenumbrüche (und bedingt auch andere white spaces) rekonstruiert werden. Kommentare sollen in einer separaten Liste mitgeführt werden. Ein Mapping zwischen Knoten in der IML bzw. Token im Tokenstrom zu den Kommentaren könnte über die Source-Location erfolgen. Zurzeit geht die Gruppe Unparse davon aus, dass sie mit den genannten Lösungsansätzen den Quelltextähnlichkeitstyp Typ 2 (vielleicht sogar Typ 1) erreichen wird. 6. Referenzen [1] M. L. Van De Vanter, Preserving the Documentary Structure of Source Code in Language-based Transformation Tools, IEEE International Workshop on Source Code Analysis and Manipulation, November 2001, http://research.sun.com/projects/jackpot/com.sun.mlvdv.do c.scam_nov01.paper_pdf.pdf [2] M. L. Van De Vanter, The Documentary Structure of Source Code,Information & Software Technology, Volume 44, Issue 13, 1 October 2002, pp. 767-782., http://research.sun.com/people/mlvdv/com.sun.mlvdv.doc.i st_02.paper_pdf.pdf [3] whitespace - http://compsoc.dur.ac.uk/whitespace/ [4] R. Falke, Ergebnisse des Projekwochenendes in Nordenham und weitere Notizen zu Arte-Fakt, 13.01.2006 Typ 3 Beliebige Unterschiede im Quelltext sind erlaubt. Lediglich die Semantik muss erhalten bleiben. Hier ist wirklich nur noch von (grober) Quelltextähnlichkeit zu sprechen.