© Excellent backgrounds/Shutterstock.com
Wie man einen bestehenden Compiler nahtlos in Eclipse integriert

Alte Sprache in neuem Gewand


Alter Compiler, integriert in einer State-of-the-Art-IDE, geht nicht? Doch! Dieser Artikel zeigt, wie ein etablierter, jahrzehntealter Compiler für eine Programmiersprache aus der Domäne der Automatisierung in Eclipse integriert und dadurch revitalisiert werden kann. Weiter beleuchten wir, wie unterschiedliche Features einer modernen IDE für die in die Jahre gekommene Sprache implementiert werden können. Möglich macht das die Verwendung des Xtext-Frameworks.

Im Umfeld der Automatisierung dreht sich alles um speicherprogrammierbare Steuerungen (SPS oder engl. PLC). Unser ganzes Leben ist von ihnen umgeben. Ob die Heizung eines modernen Gebäudes, die Fertigungsstraße eines Autoherstellers oder der sichere, autonome Betrieb einer Windkraftanlage – speicherprogrammierbare Steuerungen sind nicht mehr wegzudenken. SPS-Software wird grundlegend nicht anders als andere Software entwickelt. Es gibt dafür besonders geeignete Programmiersprachen, Entwicklungsumgebungen und natürlich eigene Compiler. Als besonders beliebte Programmiersprachesprache für Automatisierungsaufgaben hat sich über die Jahre die Sprache Strukturierter Text (ST) [1] herausgestellt. Die Sprache wurde in den frühen 1990er Jahren normiert und hat sich seit damals nur geringfügig verändert. Das spiegelt sich auch in der Toolchain wider, die noch von vielen Anwendern verwendet wird. Compiler und Entwicklungsumgebungen stammen oft noch aus derselben Zeit, samt den damals üblichen Workflows und Hilfsmitteln. Wir haben uns zum Ziel gesetzt, einen Compiler aus dieser Zeit, der durchaus noch seine Daseinsberechtigung hat, mithilfe von Eclipse und Xtext in das 21. Jahrhundert zu hieven. Die von uns entwickelte IDE trägt den Namen PLC Developer. Sie erlaubt es, auf Basis von Eclipse und mithilfe des Xtext-Frameworks Code in Strukturiertem Text auf moderne Art und Weise zu entwickeln. Dafür generiert sie eine auf XML basierende Zwischensprache (PLCopen [2]), die von dem externen Compiler gelesen und verarbeitet wird. Schlussendlich kann die kompilierte Applikation vom PLC Developer auf die SPS gespielt und ausgeführt werden (Abb. 1).

rieder_compiler_1.tif_fmt1.jpgAbb. 1: Der Workflow von der Quelldatei bis zur auf der SPS ausführbaren Applikation

Das ist Strukturierter Text

Strukturierter Text (ST) ist eine der fünf Programmiersprachen, die in der Norm IEC 61131-3 definiert sind. Die Sprache ist stark an Pascal angelehnt, wobei einige Merkmale eingeführt wurden, die sie besonders angenehm für die SPS-Programmierung macht. Listing 1 zeigt ein einfaches Programm, das eine Lampe blinken lässt. Die Applikation besteht nur aus einem so genannten Baustein (Program Organization Unit, POU), einem Programm mit dem Namen PLC_PRG. Dieses Programm ist vergleichbar mit einer Main-Methode aus Java, nur dass unsere SPS dieses Programm zyklisch aufruft, z. B. einmal pro Sekunde. Es deklariert zwei lokale Variablen. Die Variable lamp ist vom Typ BOOL und wird gleichzeitig auf einen sogenannten Kanal Q1.1 der SPS gemappt. Das bedeutet, dass wir unsere Variablen direkt an angeschlossene Aktoren (z. B. eine Lampe) und auch Sensoren (z. B. ein Taster) binden können. Auch ohne weitere Kenntnisse von ST können wir hier erkennen, dass unsere Applikation eine Lampe zum Blinken bringt, solange wir einen Taster gedrückt halten.

Listing 1: Lampe blinken lassen

PROGRAM PLC_PRG VAR lamp AT %QX1.9 : BOOL; button AT %IX1.1 : BOOL; END_VAR IF (button) THEN lamp := NOT(lamp); END_IF END_PROGRAM

Eine integrierte Entwicklungsumgebung für Strukturierten Text

Eine moderne Entwicklungsumgebung bietet Funktionalität wie Syntax-Highlighting, Content Assist, Refactorings und vieles mehr. Die meisten dieser Features verwenden einen bestimmten Teil des zugrunde liegenden Compilers. Compiler bestehen üblicherweise aus einem Lexer inklusive Parser, die aus einem Zeichenstrom, dem Quellcode, einen Abstract Syntax Tree erzeugen, einem Indexer, der die globale Namenstabelle pflegt, einem Linker, der Referenzen auflöst und schlussendlich einem Generator, der den ausführbaren Code erzeugt. All diese Bestandteile müssen bei der Umsetzung einer Sprachunterstützung berücksichtigt werden. Da es aus technologischer Sicht schwierig ist, die Einzelteile des externen Compilers aus Eclipse heraus zu verwenden, bauen wir uns einen vorgelagerten, auf Xtext basierenden Compiler, damit der PLC-Developer alle Features einer modernen IDE unterstützt.

Mithilfe einer Grammatik lassen sich die ersten Bestandteile unseres Compilers, nämlich der Lexer und der Parser, vollständig generieren. Im Xtext-Framework wird die Grammatik über eine der Extended Backus-Naur Form (ENBF) [3] sehr ähnlichen Form definiert. Über die Grammatik beschreiben wir, welche Programme syntaktisch grundsätzlich denkbar wären. Dabei ist es wichtig, dass sich die Grammatik rein mit der syntaktischen Korrektheit beschäftigt, also ob diese Sequenz an Zeichen grundsätzlich korrekt ist. Die semantische Korrektheit, also beispielsweise, ob eine Variable wirklich beschrieben werden kann, muss vom Compiler später sichergestellt werden. Sehen wir uns eine beispielhafte, etwas vereinfachte Regel aus unserer Grammatik für Strukturierten Text in Listing 2 an.

Listing 2: Regel aus der Grammatik für Strukturierten Text

VariableDeclaration: name=IDENTIFIER ('AT' channel=ChannelMapping)? ':' type=DATA_TYPE ';' ; terminal DATA_TYPE: 'INT' | 'BOOL' ; terminal IDENTIFIER: LETTER (LETTER | DIGIT)*;

Wir sehen den Auszug eine Grammatik, um Variablen zu deklarieren, also aus z. B. lamp AT %QX1.9 : BOOL zu parsen. Eine Variablendeklaration beginnt mit einem Namen, der unserer Terminalregel IDENTIFIER entsprechen muss. Wie ein IDENTIFIER auszusehen hat, ist in der zugehörigen Terminalregel definiert. Er beginnt mit einem Buchstaben, gefolgt von einer beliebigen Sequenz an Zahlen und Buchstaben. Nach dem Namen der Variable folgt ein optionales Kanalmapping, das mit dem Schlüsselwort AT eingeleitet wird. Die Definition eines Kanalmappings wollen wir uns hier sparen. Der letzte Teil unseres Beispiels ist der Datentyp der Variable. In diesem reduzierten Beispiel bieten wir nur die Typen INT und BOOL an.

Nun müssen wir also eine Grammatik entwerfen, die unsere gesamte Sprache abbildet. Daraus generiert uns das Xtext-Framework einen Lexer, einen Parser (beide mithilfe von Antlr3 [4]), ein EMF-Modell für den Abstract Syntax Tree (AST) und jeweils das Grundgerüst der restlichen Bestandteile eines Compilers (Indizierung, Linker, Validator, Generator etc.), die wir nun mit Leben füllen müssen. Auch ohne spezielle Anpassungen funktionieren nun schon einige Features wie Syntax-Highlighting, Code-Folding, syntaktische Validierung, grundlegende Refactorings und vieles mehr.

Ein wesentlicher Bestandteil eines jeden Compilers ist der Aufbau einer Symboltabelle, oft einfach Index genannt [5]. Der Index wird eigentlich von fast allen Komponenten eines modernen Compilers benutzt, um strukturiert auf Informationen des Quellcodes zuzugreifen. Jedes Element, das aus dem Quellcode heraus referenziert werden kann, muss grundsätzlich in den Index aufgenommen werden. Für unsere Programmiersprache handelt es sich hier um die üblichen Verdächtigen wie Variablen, Datentypen oder Funktionen. Durch das Implementieren einer ResourceDescriptionStrategy können wir die Indizierung einer Datei beeinflussen. Alle sprachspezifischen Charakteristika müssen bei der Indizierung berücksichtigt werden, damit darauf folgende Kompilierungsschritte, vor allem aber das Linken, erfolgreich durchgeführt werden können.

Beim Scoping geht es um eine Teildisziplin des Linkers. Der Linker ist der Teil des Compilers, der sich um das Auflösen von Referenzen bemüht. Wenn wir irgendwo in unserem Code beispielsweise eine Variable verwenden, muss der Name der Variable vom Linker aufgelöst werden, d. h. es muss das für diesen Namen deklarierte Element identifiziert werden – in unserem Fall eben die Deklaration der Variable mit diesem Namen. Ein erfolgreich gelinkter AST ist Voraussetzung für die meisten semantischen Validierungen und oft auch für die Codegenerierung am Ende des Kompiliervorgangs. Das Sc...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang