08 Xtend - HTW Dresden
Transcrição
08 Xtend - HTW Dresden
Software Factories 8 Xtend WS 2016/17 Prof. Dr. Dirk Müller Übersicht ● Motivation: pro und contra Java ● Hello-World-Beispiel ● Sichtweisen, Einordnung und Geschichte ● Beispiel „Liste von Strings in Großbuchstaben“ ● wichtige Schlüsselwörter ● Active Annotations ● Operator-Überladung ● Template Expressions ● Codegenerierung für das HelloDSL-Beispiel ● Codegenerierung für einen Prolog-zu-Scheme-Transpiler ● Zusammenfassung Dirk Müller: Software Factories WS 2016/17 2/37 Java ● Vorteile – – – – ● plattformunabhängig durch Bytecode-Ansatz, Ausführung auf JVM objektorientierte und imperative Paradigmen kombiniert mächtige Bibliotheken sehr gute integrierte Entwicklungsumgebungen (frei) verfügbar Nachteile Umwandlung einer Liste von Strings in die zugehörige Liste von Strings in Großbuchstaben – Redundanz im Quellcode mit Typangaben – funktionales Paradigma kaum umgesetzt – keine Operatorüberladung public ArrayList<String> toUpperCase(final List<String> strings) { final ArrayList<String> result = new ArrayList<String>(); for (final String string : strings) { String _upperCase = string.toUpperCase(); result.add(_upperCase); } return result; } Dirk Müller: Software Factories Quelle: [2] WS 2016/17 3/37 Hello World! package example1 class HelloWorld { def static void main(String[] args) { println('Hello World!') package example1; } import org.eclipse.xtext.xbase.lib.InputOutput; } @SuppressWarnings("all") public class HelloWorld { public static void main(final String[] args) { InputOutput.<String>println("Hello World!"); } } ● ● Xtend-Code wird beim Speichern automatisch (on-thefly) in korrespondierenden Java-Code übersetzt und im Ordner xtend-gen (anpassbar) abgelegt auffällige Einsparungen – Semikolons als Trenner zwischen Anweisungen – Rückgabetypen Dirk Müller: Software Factories WS 2016/17 4/37 Java 10 bereits heute ● flexibler und ausdrucksstarker Java-Dialekt – keine Interoperabilitätsprobleme mit Java ● ● wird zu gut lesbarem Java-5-kompatiblen (anpassbar) Code kompiliert nahtlose Nutzung bestehender Java-Bibliotheken ● Ausführung (fast) so schnell wie bei Handcodierung ● Paradigmen: objektorientiert, imperativ und funktional ● ● Typ-Inferenz, Makros (Active Annotations), LambdaAusdrücke und Operatorüberladung als einige Highlights statische Typisierung – gut für Fehlersuche und IDE-Integration Dirk Müller: Software Factories Quelle: [5] WS 2016/17 5/37 Android-GUI-Programmierung mit Xtend ● nur von Mini-Laufzeitbibliothek org.eclipse.xtend.lib abhängig, die hauptsächlich aus Google Guava besteht – Google Guava sollte sowieso genutzt werden WS 2016/17 Quelle: [7] Dirk Müller: Software Factories 6/37 Sichtweisen auf Xtend ● ● ● abgespecktes Java ideale Ergänzung zu einer mit Xtext spezifizierten DSL, um einen Übersetzer (Transpiler) oder einen Compiler selbst zu schreiben „Xtend ist ein Java-Dialekt, der mir weniger Geschwätz und mehr Funktionalität bietet“ [4] ● „Java + Xtend == Better Java“ [2] ● ein integrierender Java-Transpiler ● ein typsicherer Ersatz für Java Server Pages (JSP) [6] Dirk Müller: Software Factories WS 2016/17 7/37 Einordnung ● JVM-Sprachen nutzen die hochoptimierte Java Virtual Machine zur Ausführung von Bytecode org.eclipse.xtend.lib ● z. B. Scala, Groovy, Clojure, JRuby Scala-Quellcode (.scala) Java-Quellcode (.java) Groovy-Quellcode (.groovy) javac Clojure-Quellcode (.clj,cljs,.cljc,.edn) Quelle: [6] WS 2016/17 Xtend-Quellcode (.xtend) Vorteile ● Performanz ● einfache Integration ● einfache Fehlerbehebung via Java-Debugger Java-Bytecode (.class) JRuby-Quellcode (.rb) On-the-fly-Transpiler JVM JVM JVM Windows Linux Mac Dirk Müller: Software Factories 8/37 Geschichte ● Ursprünge in openArchitectureWare (2006-2011) – Xpand als statisch typisierte Templatesprache für JAX Innovation Award 2007: 1. Groovy 3. oAW M2C-Transformationen – Xtend als funktionale Sprache, um das Metamodell mit zusätzlicher Logik erweitern zu können (M2M-Transformationen) ● ● 2011 Xtend2 (Suffix .xtend) als Ersatz für Xpand (.xpt) und Xtend (.exp), später Namensvereinfachung zu Xtend Version 2.7.0 vom 2.9.2014 – reife Makros via Active Annotations, z. B. @Accessors ● Version 2.8.0 vom 11.3.2015 – Reverse Engineering von Java zu Xtend incl. Warnungen bei unübersetzbarem Code (FixMe-Tags) unterstützt ● Version 2.9.0 vom 1.12.2015 – Unterstützung von IntelliJ IDEA und Android Studio ● aktuelle Version 2.9.2 vom 10.03.2016 WS 2016/17 Quellen: [5][6] Dirk Müller: Software Factories 9/37 Beispiel Liste von Strings in Großbuchstaben public ArrayList<String> toUpperCase(final List<String> strings) { final ArrayList<String> result = new ArrayList<String>(); for (final String string : strings) { String _upperCase = string.toUpperCase(); result.add(_upperCase); } return result; } Java Xtend imperativ def toUpperCase(List<String> strings) { val result = new ArrayList<String>() for (string : strings) { result += string.toUpperCase } return result } Xtend funktional 10 Auftreten von „String“/„string“ sehr ausdrucksstark 3 Auftreten von „String“/„string“ def toUpperCase(List<String> strings) { strings.map( string | string.toUpperCase ) } def toUpperCase(List<String> strings) { Xtend funktional strings.map[toUpperCase] mit Lambda-Ausdrücken Quelle: [2] WS 2016/17 } Dirk Müller: Software Factories 10/37 Wichtige Schlüsselwörter ● class zur Deklaration einer Klasse – Standardsichtbarkeit ist public – package zu package private übersetzt (Standard in Java) – mehrere Public-Klassen in einer Datei erlaubt – Konstruktoren mit new statt des Klassennamens, public als Std. ● Deklaration von Klassenvariablen – Standardsichtbarkeit ist private – val zur Deklaration einer Wert-Variable (final) – var zur Deklaration einer (echten) Variable, Weglassung möglich – Weglassung des Typs erlaubt, falls Ableitung aus Initialisierung ● def zur Deklaration einer Methode – Standardsichtbarkeit ist public – Rückgabetyp kann aus dem Methodenrumpf abgeleitet werden, außer bei abstrakten und rekursiven Methoden Quelle: [5] WS 2016/17 Dirk Müller: Software Factories 11/37 Makros via Active Annotations ● schematische wiederkehrender Code (Boilerplate-Code) – Assistenten (Wizards) in IDEs Klicken – Sammlung von Code-Generatoren unübersichtlich – wie individueller Code auch handgeschrieben aufwendig ● Active Annotations als ein modernes Mittel zur Codegenerierung – einfache Integration in existierende Projekte – kürzere Durchlaufzeiten z. B. zur Erstellung von Prototypen ● ● ● Grundidee: Übersetzung von Xtend-Code in Java-Code unter Nutzung einer Bibliothek können nicht im selben Projekt genutzt werden, nur in einem vorgeschalteten oder kompiliert in einer JAR-Datei Schnittstellen im Teil org.eclipse.xtend.lib.macro der Laufzeitbibliothek definiert WS 2016/17 Quelle: [5] Dirk Müller: Software Factories 12/37 Grundlegende Idee hinter MDSD Code einer ReferenzAnwendung AnwendungsModell individueller Code generischer Code schematisch wiederkehrender Code Separierung Analyse individueller Code schematisch wiederkehrender Code Plattform Dirk Müller: Software Factories WS 2016/17 13/37 Vorgefertigte Active Annotations ● org.eclipse.xtend.lib.annotations als Plug-in / JAR ● Entwurfsmuster in Bibliothek abgelegt – aktive Weiterentwicklung (z. T. Beta-Versionen) ● ● @Accessors dient dem Anlegen von Gettern und Settern für den gekapselten Zugriff auf eine Klassenvariable @Data dient der Konvertierung einer Klasse in eine Wertobjekt-Klasse (z. B. Geldbetrag, Datum) keine Identität, nicht veränderbar, Erzeugung in gültigen Zustand alles final Getter-Methoden werden generiert, Setter-Methoden nicht Konstruktor mit Parametern für alle nicht-initialisierten Felder equals(Object)- / hashCode()-Methoden werden generiert – toString()-Methode wird generiert – – – – – ● @Delegate dient der automatischen Implementierung von Delegierungsmethoden für Schnittstellen WS 2016/17 Quelle: [5] Dirk Müller: Software Factories 14/37 Generierung von Gettern und Settern package example1 import org.eclipse.xtend.lib.annotations.Accessors @Accessors class Person { String name String firstName @Accessors(PUBLIC_GETTER, PROTECTED_SETTER) int age @Accessors(NONE) String internalField } klassenspezifische Angaben werden überschrieben WS 2016/17 Quelle: [5] package example1; import org.eclipse.xtend.lib.annotations.AccessorType; import org.eclipse.xtend.lib.annotations.Accessors; import org.eclipse.xtext.xbase.lib.Pure; @Accessors @SuppressWarnings("all") public class Person { private String name; private String firstName; @Accessors({ AccessorType.PUBLIC_GETTER, AccessorType.PROTECTED_SETTER }) private int age; @Accessors(AccessorType.NONE) private String internalField; @Pure keine Seiteneffekte public String getName() { return this.name; } public void setName(final String name) { this.name = name; } @Pure public String getFirstName() { return this.firstName; } public void setFirstName(final String firstName) { this.firstName = firstName; } @Pure public int getAge() { return this.age; } protected void setAge(final int age) { this.age = age; }} Dirk Müller: Software Factories 15/37 Operator-Überladung ● ● Ziel: besser lesbarer Code, Idee von C++ übernommen näher an der fachlichen Domäne (typisch: Mathematik), aber Ausdrucksstärke nicht verbessert (syntactic sugar) package example1 import org.eclipse.xtend.lib.annotations.Data public Complex(final @Data final u.a. Konstruktor generiert class Complex { super(); this.re = re; val double re Real- und this.im = im; val double im Imaginärteil } def +(Complex other) { new Complex(re + other.re, im + other.im) } Addition def -() { entgegengesetzte new Complex(-re, -im) Zahl (unärer Operator) } def -(Complex other) { Subtraktion als Addition this + -other der entgegengesetzten } Zahl } double re, double im) { Dirk Müller: Software Factories WS 2016/17 16/37 Test der Operatoren für komplexe Zahlen class ComplexExample { def static void main(String[] args) { val x = new Complex(1, 2) val y = new Complex(-3, 5) val z = new Complex(0, 3) alle Typen var result = -x + y += z abgeleitet result println(result.toString()) } } automatisch mit verfügbar wegen @Data mit generiert, hier über Xbase-I/O-Bibliothek aber auch als println(result) möglich package example1; import example1.Complex; import org.eclipse.xtext.xbase.lib.InputOutput; @SuppressWarnings("all") public class ComplexExample { public static void main(final String[] args) { final Complex x = new Complex(1, 2); final Complex y = new Complex((-3), 5); final Complex z = new Complex(0, 3); Complex _minus = x.operator_minus(); Complex result = _minus.operator_plus(y); Complex _result = result; result = _result.operator_plus(z); String _string = result.toString(); InputOutput.<String>println(_string); } } Dirk Müller: Software Factories WS 2016/17 17/37 Template Expressions ● Ziel: gut lesbare String-Verkettung – wichtig für Compiler/Transpiler ● ● von Paar dreifacher Anführungszeichen (''') abgegrenzt interpolierte Ausdrücke von französischen Anführungszeichen («Guillemets») umschlossen – – – – Zeichensatz sollte UTF-8 sein weniger Probleme mit Escape-Konflikten über Syntax-Vervollständigung angeboten explizit (unter Windows) mit CTRL+< bzw. CTRL+> ● können überall stehen (alles ist ein Ausdruck) ● Typ ist CharSequence, wird ggf. in String umgewandelt Dirk Müller: Software Factories WS 2016/17 18/37 FOR-Schleifen in Templates – BEFORE fügt Vorspann-Text ein – AFTER fügt Abspann-Text ein – SEPARATOR fügt Text zwischen zwei aufeinanderfolgenden Iterationen ein (Trenner) – BEFORE und AFTER nur ausgeführt, wenn min. 1 Iteration – SEPARATOR nur ausgeführt, wenn min. 2 Iterationen Dirk Müller: Software Factories WS 2016/17 19/37 Whitespace-Behandlung in Xtend-Templates <html> <body> Traffic light colors: <ul> <li>red</li> <li>yellow</li> <li>green</li> </ul> </body> </html> println(new HelloWorld().html) ● ● ● Formatierung bei Templatesprachen klassisch über Nachschaltung eines Formatter bzw. Pretty Printer gelöst Code des Generators und des gen. Codes übersichtlich Xtend-Ansatz: Unterscheidung, welche Einrückungen für Ausgabe gedacht sind und welche nicht (Kontrollstrukturen wie z. B. FOR oder IF), sichtbar per Syntax-Hervorhebung Dirk Müller: Software Factories WS 2016/17 20/37 Einhaken in den Generator-Mechanismus Datei anlegen und ersten Text dort eintragen liefert einen BaumIterator <Eobject> als Sicht auf diese Ressource funktional: filtern, Namen extrahieren, mit Kommas trennen Dirk Müller: Software Factories WS 2016/17 21/37 Codegenerierung in Java übersetzt Quelldatei in der Sprache Xtend generierte Zieldatei in Java Dirk Müller: Software Factories WS 2016/17 22/37 Test der Codegenerierung Test in zweiter Eclipse-Instanz Speichern als Trigger für die Codegenerierung Dirk Müller: Software Factories WS 2016/17 23/37 Zielcode generiert Zieldatei Dirk Müller: Software Factories WS 2016/17 24/37 Neuer Eintrag im Quellcode Dirk Müller: Software Factories WS 2016/17 25/37 Konsistente Änderung im Zielcode Dirk Müller: Software Factories WS 2016/17 26/37 Liste zu grüßender Personen mit Templates override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) { fsa.generateFile('greetings.txt', 'People to greet: ' + resource.allContents .filter(typeof(Greeting)) .map[name] .join(', ')) ohne Templates } override void doGenerate(Resource resource, mit Templates IFileSystemAccess2 fsa, IGeneratorContext context) { fsa.generateFile('greetings.txt', 'People to greet: ' + ''' «FOR greeting : resource.allContents .filter(Greeting) .toIterable SEPARATOR ', '»«greeting.name»«ENDFOR» ''') wichtig, dass in 1 Zeile, sonst Umbrüche in der Liste } Dirk Müller: Software Factories WS 2016/17 27/37 Einstellungen für den Xtend-Transpiler Java-Version für Zielcode Warnungen unterdrückt Zielverzeichnis Nachfrage beim Versuch, generierte Dateien zu editieren Dirk Müller: Software Factories WS 2016/17 28/37 Beispiel: Lied „99 Bottles of Beer“ 99 Bottles of Beer 99 bottles of beer on the wall, 99 bottles of beer. Take one down and pass it around, 98 bottles of beer on the wall. 98 bottles of beer on the wall, 98 bottles of beer. Take one down and pass it around, 97 bottles of beer on the wall. [..] 2 bottles of beer on the wall, 2 bottles of beer. Take one down and pass it around, 1 bottle of beer on the wall. „One“ als Variante „one“ „one“ als Variante 1 bottle of beer on the wall, 1 bottle of beer. Take one down and pass it around, no more bottles of beer on the wall. No more bottles of beer on the wall, no more bottles of beer. Go to the store and buy some more, 99 bottles of beer on the wall. WS 2016/17 Quelle: [8] Dirk Müller: Software Factories 29/37 Bottles-Song in BASIC 10 20 30 40 50 60 REM BASIC Version of 99 Bottles of beer FOR X=100 TO 1 STEP -1 PRINT X;"Bottle(s) of beer on the wall,";X;"bottle(s) of beer" PRINT "Take one down and pass it around," Singular/Plural unsauber PRINT X-1;"bottle(s) of beer on the wall" NEXT Endvers nicht abgebildet 10 REM Basic version of 99 bottles of beer 20 REM Modified by M. Eric Carr ([email protected]) 30 REM from prior version found on this site. 40 REM (Modified to correct "1 bottle" grammar) 50 FOR X=99 TO 1 STEP -1 60 PRINT X;"bottle"; GOSUB-Unterprogramm nicht genutzt 70 IF X<>1 THEN PRINT "s"; 80 PRINT " of beer on the wall,";X;"bottle"; 90 IF X<>1 THEN PRINT "s"; 100 PRINT " of beer" 110 PRINT "Take one down and pass it around," 120 PRINT X-1;"bottle"; 130 IF X<>1 THEN PRINT "s"; 140 PRINT " of beer on the wall" Endvers nicht abgebildet 150 NEXT WS 2016/17 Quelle: [8] Dirk Müller: Software Factories 30/37 Bottles-Song in Java class bottle { public static void main(String args[]) { String s = "s"; for (int beers=99; beers>-1;) { System.out.print(beers + " bottle" + s + " of beer on the wall, "); System.out.println(beers + " bottle" + s + " of beer, "); if (beers==0) { System.out.print("Go to the store, buy some more, "); System.out.println("99 bottles of beer on the wall.\n"); System.exit(0); } else „0 bottles“ statt System.out.print("Take one down, pass it around, "); „no more bottles“ s = (--beers == 1)?"":"s"; System.out.println(beers + " bottle" + s + " of beer on the wall.\n"); } } ternärer Operator } WS 2016/17 Quelle: [8] Dirk Müller: Software Factories 31/37 Bottles-Song in Xtend class BottlesSong { def static void main(String[] args) { println(new BottlesSong().singTheSong(99)) } Template Expression def singTheSong(int all) ''' «FOR i : all .. 1» «i.Bottles» of beer on the wall, «i.bottles» of beer. Take one down and pass it around, «(i - 1).bottles» of beer on the wall. «ENDFOR» No more bottles of beer on the wall, no more bottles of beer. Go to the store and buy some more, «all.bottles» of beer on the wall. ''' def static bottles(int i) { switch i { case 0 : 'no more bottles' 1 als case 1 : 'one bottle' Zahlwort default : '''«i» bottles''' }.toString } def static Bottles(int i) { bottles(i).toFirstUpper } } Powerful Switch Expression Template Expression Extension method Everything is an expression. Dirk Müller: Software Factories WS 2016/17 32/37 Codegenerierung für einen Prolog-zu-Scheme-Transpiler /* * generated by Xtext 2.9.2 */ package de.htwdd.sf.beleg.muellerd.generator import import import import Xtext generiert eine Schablone org.eclipse.emf.ecore.resource.Resource org.eclipse.xtext.generator.AbstractGenerator org.eclipse.xtext.generator.IFileSystemAccess2 org.eclipse.xtext.generator.IGeneratorContext /** * Generates code from your model files on save. * * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#code-generation */ class PrologDslGenerator extends AbstractGenerator { Callback-Routine, die beim Speichern } override void doGenerate( Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) { } muss noch gefüllt des Quelltextes aufgerufen wird AST vom Parsen mittels Xtext geliefert Zugriff aufs Dateisystem werden Dirk Müller: Software Factories WS 2016/17 33/37 Implementierung von doGenerate class PrologDslGenerator extends AbstractGenerator { String code // zur Konstruktion des auszugebenden Textes override void doGenerate(Resource resource, IFileSystemAccess2 fsa, Filtern des passenden IGeneratorContext context) Typs und iterierbar { machen notwendige Initialisierung zum leeren String code = "" for (e : resource.allContents.filter(PrologDsl).toIterable) { Kontrollausgabe System.out.println("Start Transformieren") in der Konsole der e.traversiere Methode, die noch für alle ersten Eclipse-Instanz Typen implementiert wird } fsa.generateFile("PrologDsl.lsp", code) Herausschreiben des } Endergebnisses def conc(String str) { code = code + str; } } // zum Anfügen neuer Programmteile Hilfsmethode Dirk Müller: Software Factories WS 2016/17 34/37 Implementierung einer Regel in Xtend <prologdsl> → ( prolog (quote <program>) (quote <query> )) def traversiere(PrologDsl k) { conc("(prolog (quote") noch zu implementieren k.program.traversiere Zeilenumbruch zur besseren Lesbarkeit conc(")\r\n(quote") k.query.traversiere noch zu implementieren conc("))") } Besser lesbare Lösung mittels Templates möglich? Dirk Müller: Software Factories WS 2016/17 35/37 Zusammenfassung ● Xtend als statisch typisierte, auf Java aufbauende Sprache – Typen können oft abgeleitet werden – funktionales Paradigma neben dem objektorientierten und imperativen unterstützt – alles ist ein Ausdruck => flexible Komponierbarkeit – sehr kompakter und ausdrucksstarker Code ● gute Eignung zur Code-Generierung – Implementierung der Methode doGenerate ● gute Template-Unterstützung – flexible Kontrollstrukturen – besonders gut gelungen: Behandlung von Whitespaces mit guter Lesbarkeit im Code-Generator und im generierten Code ● Makros für schematisch wiederkehrenden Code via Active Annotations und Operatorüberladung als Highlights Dirk Müller: Software Factories WS 2016/17 36/37 Literatur [1] [2] [3] [4] [5] [6] [7] [8] Hartmut Fritzsche, „Software Factories – Skript zur Lehrveranstaltung“, 11.01.2016, Download am 6.4.2016, http://www2.htw-dresden.de/~fritzsch/SF/Software_Factories_Skript.pdf Sven Efftinge, Sebastian Zarnekow: „Extending Java“, in: Pragmatic Programmer Magazine, Dezember 2011, Download am 30.04.2016, https://pragprog.com/magazines/2011-12/extending-java Alex Blewitt: „Xtend Extends Java“, in: InfoQ, Juni 2012; (Interview mit Sven Efftinge), Download am 30.04.2016, http://www.infoq.com/news/2012/06/xtend-release-10 Jan Koehnlein auf Twitter, 21.04.2016, Download am 30.04.2016, https://twitter.com/JAXenter/status/723098924114833408 Xtend-Dokumentation, https://eclipse.org/xtend/index.html Meinte Boersma: „Meinte's DSL-Blog: Using Xtext’s Xtend(2) language“, 19.09.2011, Download am 3.5.2016, https://dslmeinte.wordpress.com/2011/09/19/using-xtexts-xtend2-language/ Sven Efftinge: „sven eftinge's blog: Writing Android UIs with Xtend“, 12.12.2011, Download am 6.5.2016, http://blog.efftinge.de/2011/12/writing-android-uis-with-xtend.html Oliver Schade: „99 Bottles of Beer“, 2016, http://99-bottles-of-beer.net Dirk Müller: Software Factories WS 2016/17 37/37