Direkt zum Hauptbereich

Einführung in Xbase mit Xtext : Javatypen in Xtext

Folgendes Problem:
Ich hab irgendwo Xtext gelernt. In der Uni, in einem Internet-Tutorial oder beim Surfen auf dem Pott. "Warum nicht einfach mal ne Sprache entwickeln, die statt der bekannten Keywords, Scrubs-Zitate benutzt? Da ist Xtext schließlich da!" - Das ist korrekt. Allerdings muss man dann ja das komplette Scoping, Typenberechnung, Validierung und Codegeneration selbst übernehmen. Möchte ich das? Nein. Eine mögliche Lösung hierfür ist Xbase.


Was ist Xbase?

Xbase ist eine Erweiterung von Xtext. Hierbei wird das Javatypsystem benutzt, um die Vorteile dessen zu nutzen. Die Typisierung, das Scoping und auch die Validierung ("Kann ich dieser Variable diesen Wert zuordnen?") wird hierbei bereits von Haus aus vorgegeben. Weiterhin werden auch IDE-Features wie der Debugger out-of-the-box mitgeliefert. Weitere Details hierzu sind in der Dokumentation hier zu finden.


Erste Schritte in Xbase

1. Voraussetzungen

Zunächst wird eine laufende Version von Eclipse mit den DSL-Developer-Tools benötigt. Weiterhin werden Kenntnisse des Compilerbaus, Grammatiken, Parsing und Xtext im Allgemeinen vorausgesetzt.

2. Erste eigene Sprache

Zunächst muss ein neues Xtext Projekt angelegt werden. Die Standardmäßig generierte Xtext-Datei wird um folgendes ergänzt:

grammar org.xtext.xbase.Scrubs with org.eclipse.xtext.xbase.Xbase

Dadurch wird Xbase importiert.
Unser Ziel ist es nun, eine Grammatik zu schreiben, die Klassen erzeugt und in den Klassen Felder definieren lässt. So soll folgender Code möglich sein:

jambalaja MeineKlasse {
    bananarama jd int
    bananarama turk double
    bananarama carla boolean
}

3. Javatypen in der Grammatik

Die Grammatik hierfür sieht folgendermaßen aus:

Scrubs:
 classes+=Class*;
Class:
 'jambalaja' name=ID '{'
 variables+=Variable* 
 '}';
Variable:
 'bananarama' name=ID type=JvmTypeReference;

Der besondere, neue Teil ist der type der Variablen, nämlich einer JvmTypeReference. Dies entspricht, vereinfacht gesagt, einem Java-Datentypen. Das kann zum Beispiel int, double, float oder boolean sein. Nach dem Erfolgreichen Ausführen des mw2-Workflows, werden eine Menge zusätzlicher Klassen erzeugt.
Beim Testen der Sprache stellt sich heraus, entweder durch einen Unit-Test oder zunächst einer Eclipse-Instanz(wichtig: Es muss ein Java-Projekt erstellt werden und die DSL-File im src Verzeichnis liegen!), stellt sich heraus, dass das oben gezeigte Beispiel erfolgreich geparsed wird. Nur wird kein Code generiert. Warum?

Der JvmModelInferrer

Zur Codegeneration liegt im Package jvmmodel der JvmModelInferrer. Dieser ist dafür zuständig, den gewünschten Javacode zur DSL zu erzeugen. Er beschreibt das Mapping der DSL auf Java-Elemente. Der Code für unser Beispiel ist folgender:


def dispatch void infer(Scrubs element, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {

  for(classes : element.classes){
   acceptor.accept(classes.toClass(classes.name))[
    for(member : classes.variables){
     val field = member.toField(member.name, member.type)
     members += field
    }]}}

Zur Erklärung: Der Code iteriert über alle Klassen des Programmes und erzeugt für jede eine Javaklasse, damit auch eine .java File im src-gen Ordner. Für jede Klasse werden darin die Variablen als Felder erzeugt. Die Klasse zum erzeugten Code sieht folgendermaßen aus:

public class MeineKlasse {
  private int jd;  
  private double turk; 
  private boolean carla;
}




Fazit und Ausblick

Grundsätzlich ist zu sehen, dass die Codegenerierung und das Einbinden von Xbase sehr einfach ist. Das Verwenden von Javatypen und damit der richtigen Typberechnung ist trivial. Allerdings ist der Code noch nicht ausführbar. Hierfür fehlen Operationen, die über XExpressions realisiert werden können. Außerdem lässt sich Xbase erweitern und fast alles lässt sich auf die eigenen Bedürfnisse anpassen. Mehr dazu im nächsten Beitrag.

Wer sich für das Thema interessiert, sei das Buch "Implementing Domain Specific Languages with Xtext and Xtend" von Lorenzo Bettini sehr empfohlen.

Kommentare

Beliebte Posts aus diesem Blog

Konzepte: Smartpointer in C++

Heute habe ich mich, seit längerer Zeit, im Rahmen einer Vorlesung mal wieder mit C++ befasst. Neben der Syntax zur Initialisierung von Klassen (insbesondere der Zuweisung der Inhalte an die Membervariablen, was das Konstruktorschreiben wesentlich erleichtert) waren Smartpointer ein relevantes Thema. Besonders befasst habe ich mit mit unique_ptr bzw. auto_ptr und shared_ptr , auf deren Konzepte ich kurz genauer eingehen möchte. Smartpointer? Smartpointer sind Pointer, die die normalen C-Referenzen wrappen und dadurch eine gewisse Garbage-Collection darstellen. Sie versuchen sicher zu stellen, dass keine mehrfachen deletes auf Zeiger aufgerufen werden oder Speicherlecks durch fehlende deletes zu vermeiden. Unique-ptr Unique-pointer, bzw. in älteren Fassungen auto-ptr (Referenz: Hier ) dienen dazu, einen eindeutigen Eigentümer für Pointer zu bestimmen. Mehrere Eigentümer sollten nicht erlaubt sein. Das Konzept ist aber nur hilfreich, wenn der Besitzer klar bestimmbar ist. Ein ei

Basic Type Mapping mit Xbase

Folgendes Problem: Die Bezeichnungen "int" oder "double" sind einfach hässlich. Welcher "normale" Mensch weiß denn, was double bedeutet? .. Ok, ich gehe davon aus, dass jeder Leser hier tatsächlich weiß, wofür das steht. Dennoch möchte man eventuell den primitiven Datentypen andere Namen geben. Zum Beispiel zum Entwicklung einer Programmiersprache bestehend aus "How I Met Your Mother"-Zitaten, bei der "Ted" für den Datentyp "int" und "Robin" für "double". steht. Nun zum technischen: Zunächst muss die Variablendeklaration definiert werden. Diese wird anschließend als zusätzliche XPrimaryExpression hinzugefügt. Variable returns XVariableDeclaration: {Variable} writeable?='variable' simpleName=ID ':' typing=('Ted'|'Robin' /* more types */); Normalerweise wird bei Xbase im feld type der XVariableDeclaration eine JvmTypeReference gesetzt, die auf den Java Typen zeigt. D