Skip to content

Latest commit

 

History

History
88 lines (53 loc) · 13.9 KB

prerequisites.md

File metadata and controls

88 lines (53 loc) · 13.9 KB

>> Title << >> Preface << >> Contents << >> Bibliography <<


Konzeptionelle und technische Voraussetzungen

Die Grundlagen von RACR-NET lassen sich in zwei Kategorien scheiden: Übersetzerbaukonzepte zur Spezifikation formaler Sprachen und Generierung entsprechender Sprachprozessoren und Technikräume, welche zur Implementierung genutzt wurden. Die RACR Scheme-Bibliothek setzt hierbei sowohl den konzeptionellen, als auch den technischen Rahmen fest: RAG-gesteuerte Graphersetzung und die Programmiersprache Scheme. Als Technikraum für eine objektorientierte Adaption RACRs wurde C# und somit das .NET-Framework gewählt.

Überblick der RAG-gesteuerten Graphersetzung

Attributgrammatiken [Knuth1968,Knuth1971,Paakki1995] sind eine wichtige Technik im Übersetzerbau [Lam2006]. Attribute bilden ein Gleichungssystem zur deklarativen Spezifikation kontextsensitiver Analysen basierend auf einer kontextfreien Grammatik. Somit ermöglichen Attributgrammatiken die Spezifikation der Semantik kontextfreier Sprachen.

Schwächen zeigen Attributgrammatiken bei der Berechnung nichtlokaler, bedingter Relationen und aggregierter Attributwerte [Hoover1986]. Zum Beispiel müssen für die Namensanalyse typischer Programmiersprachen, wie C++ oder Java, Informationen aus dem Syntaxbaum bezüglich der Deklarationen gesammelt und in den jeweiligen Attributen wiederholt zwischengespeichert werden. Diese Aggregat-Attribute können sehr komplex werden, was die Erweiterbarkeit der Grammatik erschwert – besonders für Sprachen mit komplizierten Sichtbarkeitsbereichen der Variablen, wie zum Beispiel objektorientierten Sprachen.

Referenzattributgrammatiken adressieren dieses Problem durch die Einführung von Referenzattributen – Attribute, deren Wert Referenzen auf beliebige Knoten innerhalb des Syntaxbaums sind [Hedin2000]. Mittels Referenzattributen wird der Syntaxbaum gewissermaßen mit zusätzlichen Kannten versehen und zu einem Syntaxgraph erweitert. Über Referenzen können auf die Attribute entfernter Knoten zugegriffen werden. Auf diese Weise kann Information von einem Knoten zu einem anderen entfernten Knoten im Syntaxbaum direkt übertragen werden und muss nicht in komplexen Aggregat-Attributen gehalten werden.

Für effiziente inkrementelle Attributsauswertungen ist es entscheidend, dass Attribute nicht unnötig mehrfach berechnet werden [MaddoxIII1997]. Um Mehrfachauswertungen zu vermeiden, bietet es sich an, einmal berechnete Attributwerte in einem Cache-Speicher zu halten. Veränderungen am abstrakten Syntaxbaum (AST) erfordern jedoch eine Neuberechnung jener Attribute, die direkt oder indirekt von der modifizierten Baumstruktur abhängen. Deshalb müssen gegebenenfalls die Attribut-Caches invalidiert werden. Inkrementelle Attributsauswertung wurde bislang nur für statische Auswertungsverfahren realisiert [Buerger2012,Soederberg2012]. Referenzattributgrammatiken erfordern jedoch eine dynamische Auswertung, da die Abhängigkeiten von auf Referenzattributen basierenden Attributen statisch unbekannt sind. Die inkrementelle Auswertung von Referenzattributgrammatiken war ein bis vor kurzem ungelöstes Problem.

Am Lehrstuhl Softwaretechnologie der Technischen Universität Dresden wurde von Christoff Bürger ein technisches Verfahren namens Referenzattributgrammatik-gesteuerte Graphersetzung [Buerger2012,Buerger2015b] entwickelt, das effiziente, inkrementelle Auswertung von Referenzattributen, sowie den Einsatz von Attributgrammatik-basierten Analysen zur Graphersetzungen ermöglicht. Attributsabhängigkeiten werden dynamisch während der Auswertung überwacht. Bei einer Graphtransformation werden lediglich die Caches der Attribute invalidiert, die von der Struktur des geänderten Teilbaumes abhängen. Entsprechend werden in weiteren Analysen nur diese Attribute neu berechnet.

Scheme

Scheme ist die Implementierungssprache von RACR. Sie ist dynamisch, vorwiegend funktional und bekannt für ihr elegantes, minimalistisches Design.

Es existieren viele Interpreter und Compiler für Scheme, die jedoch häufig von offiziellen Sprach-Standards abweichen. Deshalb wird Scheme oft als Überbegriff für eine ganze Familie von Dialekten angesehen. Das Scheme Steering Committee, ein aus drei Personen bestehendes Gremium, das den Standardisierungsprozess der Sprache übersieht, selbst bezeichnete Scheme als die unportierbarste Sprache der Welt [Clinger2009].

Die verschiedenen Versionen der Sprache werden Revised n Report on the Algorithmic Language Scheme (RnRS) genannt. Der R6RS [Sperber2009] brachte grundlegende, teils kontroverse Änderungen mit sich. Um den Anforderungen der modernen Softwareentwicklung gerecht zu werden, wurde ein geregeltes Modulsystem eingeführt und die Sprache mit Standardbibliotheken ausgestattet. Da die Bibliotheken Teil des Standards selbst sind, ist der R6RS im Vergleich zu seinem Vorgänger sehr viel umfangreicher, was im Widerspruch zu der minimalistischen Philosophie der Sprache steht. Es finden sich nur wenige vollständig R6RS-konforme Scheme-Implementierungen.

Der aktuelle R7RS [Shinn2013] teilt Scheme in zwei getrennte, zueinander kompatible Sprachen auf: eine kleine, pädagogische Sprache mit minimalistischem Charakter und eine moderne, zweckmäßige Programmiersprache zur Softwareentwicklung, die den R6RS ersetzen soll. Die kleine Sprache umfasst die wichtigsten Features des R6RS, wie Record-Typen, ein Bibliothekssystem und Exceptions, verzichtet aber auf viele zusätzliche, umfangreiche Bibliothekteile des R6RS.

Die wichtigsten Spracheigenschaften Schemes sind eine dynamische Typisierung, imperative Schreiboperationen, lexikalische Hüllen und First-Class-Prozeduren, automatische Speicherverwaltung und hygienische Makros. Scheme zwingt Programmierern kein spezifisches Paradigma, wie zum Beispiel logische, funktionale oder objektorientierte Programmierung, auf. Vielmehr ist Scheme dank seines mächtigen Makrosystems und der schlichten, prägnanten Syntax eine programmierbare Programmiersprache. Gewünschte Sprachkonzepte und Abstraktionen werden in Scheme selbst hineinprogrammiert.

Aufgrund der dynamischen Typisierung und der automatischen Speicherverwaltung Schemes, welche die Freigabe von dynamisch allozierten Speicherplatz erfordert, wenn dieser nicht mehr benötigt wird, stellen Scheme-Implementierungen meist eine eigene Laufzeitumgebung zur Programmausführung bereit (virtuelle Maschine). Scheme-Implementierungen tendiert daher eher zu einem Interpreter- statt Übersetzeransatz.

Die RACR Scheme-Bibliothek

RACR [Buerger2012] ist die Referenzimplementierung der RAG-gesteureten Graphersetzung. Hierbei handelt es sich um eine R6RS-konforme Scheme-Bibliothek, welche in dieser Arbeit in C# integriert werden soll. Die in ihr enthaltenen Prozeduren lassen sich in folgende Bereiche aufgliedern:

  • Definition von abstrakten Syntaxbaum-Schemata mithilfe von Nichtterminal-Klassen, Kompositen, typisierten Kind-Elementen und Vererbung zwischen Nichtterminal-Klassen (erweiterte Backus-Naur-Form [Scowen1998], basierend auf Vererbung zwischen Nichtterminalen [Hedin1989])
  • Attribuierung der Nichtterminale unter Nutzung normaler Scheme-Prozeduren als Attributgleichungen
  • Erzeugung von Syntaxbäumen für eine bestimmte Sprachspezifikation
  • Baum-Traversierung und Abfrage von Attributen und Knoten-Informationen (Die hierfür von RACR bereitgestellten Funktionen werden innerhalb von Attributgleichungen zur Berechnung derselben genutzt.)
  • Annotation von AST-Knoten
  • Graphersetzung

Eine RACR-Anwendung kann für gewöhnlich in zwei Teile gegliedert werden. Der erst Teil umfasst die Spezifikationsphase der Sprache, in welcher zuerst Grammatikregeln und danach Attribute definiert werden. Attributgleichungen werden von Prozeduren verkörpert, die den Attributwert für einen AST-Knoten errechnen. Bemerkenswerterweise geschieht die Sprachdefinition zur Programmlaufzeit, was die Generierung von Sprachen abhängig von Laufzeitinformationen erlaubt. Insbesondere wird keine Typ-Hierarchie über die Nichtterminale angelegt. Stattdessen bietet RACR für Nichtterminale jedes Typs eine generische Schnittstelle. Attribute sind somit nicht an einen bestimmen Typ gekoppelt. Sie können an verteilten Stellen im Programm definiert werden. Attribute sind dynamische Inter-Typ-Deklarationen gemäß der aspektorientierten Programmierung [Avgustinov2008], eine bewährte Technik für die Implementierung erweiterbarer Sprachprozessoren [Ekman2006].

Nachdem die Sprache spezifiziert wurde, können ASTs instanziiert werden und Attribute von AST-Knoten ausgewertet werden. Die inkrementelle Attributsauswertung, der wichtigste Vorteil von RACR gegenüber vielen anderen Referenzattributgrammatik-Werkzeugen, kommt bei Graphersetzungen und anschließenden Attributsauswertungen zum Tragen. Gleichzeitig profitiert die Graphersetzung von attributbasierten Analysen. Beispielsweise kann der Wert von Attributen eine Prozedur sein, welche Ersetzungsregel-Anwendungen kapselt. Attribute können also zur Ableitung von Ersetzungen genutzt werden. Ein Vorteil dieses Ansatzes ist, dass die Mustererkennung (das Subgraphisomophismus-Problem [Ullmann1976]) der Graphersetzung in Attribute kodiert ist und damit ebenfalls inkrementell erfolgt.

Das in Abbildung 2.1 gezeigte Zustandsdiagramm stellt alle Prozeduren RACRs in Beziehung.

RACR-API

Abbildung 2.1: RACRs API [Buerger2012]

Das .NET-Framework und die Common Language Infrastructure

.NET ist ein von Microsoft entwickeltes Software-Framework und eine integrale Komponente des Windows-Betriebssystems. Für .NET entwickelte Programme werden nicht direkt nach Machinencode, sondern nach Bytecode – der Intermediate Language (IL) – übersetzt und laufen innerhalb eines virtuellen Ausführungssystems. Programmiersprachen, die nach IL kompilieren, werden .NET-Sprachen genannt, wobei C# und Visual Basic .NET zu den wichtigsten Vertretern gehören. Der IL-Code wird in sogenannten Assemblies in Form von EXE-Dateien (Prozess-Assemblies) oder DLL-Dateien (Library-Assemblies) gespeichert.

Die stapelbasierende virtuelle Maschine (VM) von .NET wird innerhalb der Common Language Infrastructure (CLI) [ECMA2012], einem internationalen Standard, definiert und bietet sprach-neutrale Features, wie zum Beispiel automatische Speicherbereinigung, Exceptions, Typsicherheit, Zugriff auf eine umfangreiche Klassenbibliothek und Just-in-Time-Kompilierung, wobei entsprechend der zu Grunde liegenden Rechnerarchitektur IL-Code bedarfsgesteuert zu nativem Code übersetzt wird. Neben der Common Language Runtime (CLR), der Microsoft-Implementierung der CLI, existieren Portable.NET und das Mono-Projekt als kompatible, quelloffene Alternativen.

NET-to-machine-code

Abbildung 2.2: Übersetzung von .NET-Sprachen nach Maschinencode

Abbildung 2.2 veranschaulicht die Kompillierungsprozesse, die der Ausführung eines .NET-Programms vorhergehen müssen. Alle .NET-Programme haben gleichen Bytecode, unabhängig von der Implementierungssprache oder der Zielarchitektur. Dies hat folgende wichtige Konsequenzen zur Folge:

  • .NET-Programme sind nicht architekturgebunden, sondern plattformunabhängig. Ein unter Windows entwickeltes Programm kann nach der Kompilierung unter Linux (mittels Mono) ausgeführt werden und umgekehrt.
  • .NET-Sprachen haben zueinander hohe Interoperabilität. Die CLI vereinheitlicht unter anderem Exception-Handling, den Aufbau von und Zugriff auf Klassenbibliotheken und die Interaktion zwischen allen Datentypen. Objektinstanzen können über Sprachgrenzen hinweg ausgetauscht werden.

IronScheme

IronScheme ist eine quelloffene, weitgehend R6RS-konforme Scheme-Implementierung für die .NET Software-Plattform. Sie umfasst einen eigenständigen, interaktiven Interpreter zur Ausführung von Scheme-Programmen und eine Klassenbibliothek, mittels welcher IronScheme in andere .NET-Projekte eingebunden werden kann. Diese Klassenbibliothek erlaubt es, .NET–Strings (genauer gesagt handelt es sich um den Typ System.String – die Klasse String im Namensraum System) als Scheme-Ausdruck auszuwerten, analog zu der Eval-Funktion, wie sie in vielen dynamischen Sprachen zu finden ist. So wird während der Programmausführung aus dem Scheme-Code IL-Code generiert und dieser anschließend ausgeführt. Scheme-Bibliotheken können jedoch auch zu Assemblies vorkompiliert werden.

Scheme .NET
fixnum System.Int32
flonum System.Double
boolean System.Boolean
string System.String
pair IronScheme.Runtime.Cons
'() null
hashtable System.Collections.Hashtable
symbol Microsoft.Scripting.SymbolId
procedure Ironscheme.Runtime.Callable

Tabelle 2.3: IronSchemes Abbildung von Scheme-Datentypen auf .NET-Datentypen

Viele wichtige Datentypen sind als Adapter für .NET-Datentypen realisiert, was die Interoperabilität von IronScheme zu anderen .NET-Sprachen erhöht. Andere Datentypen implementieren spezielle Schnittstellen. Die für diese Arbeit relevanten Scheme-Typen und deren zugehörige .NET-Typen sind in Tabelle 2.3 aufgeführt.