Xtext – Programmiersprachen selbst gemacht

Habt ihr euch schon mal überlegt eure eigene Programmiersprache zu entwickeln? Falls ja, dann ist vielleicht Xtext genau das richtige für euch. Mit diesem Eclipse-Plugin könnt ihr auch mittels einer vereinfachten EBNF eure eigene Domain Specific Language (DSL) entwickeln, wobei euch XText hierfür auch gleich noch Werkzeuge wie Parser und Editor mit Syntax Highlighting oder Code Completion generiert. Dadurch lassen sich DSLs mit erstaunlich geringem Aufwand realisieren. Wenn ihr eine kurze Geschmacksprobe davon haben wollt, dann versucht doch einfach mal die kurzen Tutorials von Xtext aus, welche in nur wenigen Minuten durchgeführt sind.

Denkbar sind auch Szenarien wie Code-Generierung, welche mit der in Xtext integrierten Sprache Xtend durchgeführt werden können. Xtend kann man sich wie eine Mixtur aus Java und C# vorstellen, welche dabei jedoch eine noch kompaktere Syntax aufweisen kann. Was sich hierdurch sehr leicht realisieren lässt sind beispielsweise das Grundgerüst für Klassen, Methoden oder Properties, welche oft über sehr ähnliche oder wiederkehrende Struktur aufweisen. Hier lässt sich beispielsweise mit wenig Aufwand eine DSL entwickeln, welche die Methoden mit JavaDoc-Kommentaren dieses Klassen kompakt formuliert.

grammar org.xtext.de.htwg.ModelGen with org.eclipse.xtext.common.Terminals
generate modelGen "http://www.xtext.org/de/htwg/ModelGen"

ModelGen:
'PACKAGE' name=QualifiedName
'NAME' pluginName=ID
elements+=RootElement*
;

RootElement:
PackageDeclaration | Type | Import
;

AbstractElement:
Type | Import
;

QualifiedName:
ID ('.' ID)*
;

QualifiedNameWithWildcard:
QualifiedName '.*'?
;

Type:
Typedef | Interface
;

Typedef:
'type' name=ID
;

PackageDeclaration:
'package' name=QualifiedName '{'
elements+=AbstractElement*
'}'
;

Import:
'import' importedNamespace = QualifiedNameWithWildcard
;

Interface:
'interface' name=ID '{'
methods+=Method*
'}'
;

Method:
'method' name=ID
'('(params+=Parameter
(',' params+=Parameter)*)?')'
':' type=[Type | QualifiedName]
;

Parameter:
name=ID ':' type= [Type | QualifiedName]
;

Xtext erzäugt dann aus dieser EBNF-Grammatik die entsprechenden Artefakte und das EMF-Model, um unseren Generator mit mit Informationen füttern zu können. Eine Instanz unserer DSL könnten nun beispielsweise wie folgt aussehen:

PACKAGE de.htwg.seapal.person
NAME Person

type void
type String
type boolean
type long

package model {
interface IDatabase {
method save(data: String): void
method delete(id: long): boolean
method get(id: long): String
method close(): void
}
}

Für die Code-Generierung kann nun die in Xtend integrierte Template-Engine verwendet werden, welche das Generierung von Code wesentlich übersichtlicher gestalten lässt als mit einem StringBuilder mit reinem Java-Code.

// ...
override void doGenerate(Resource resource, IFileSystemAccess fsa) {
// Find root package name
for (e: resource.allContents.toIterable.filter(typeof(Plugin))) {
rootPackage = e.fullyQualifiedName;
pluginName = e.name.toLowerCase().toFirstUpper();
}

// Generate interfaces
for (e: resource.allContents.toIterable.filter(typeof(Interface))) {
fsa.generateFile(
e.fullyQualifiedName.toString("/") + ".java",
e.compileInterface)
}
// ...
}

def compileInterface(Interface iface)'''
«IF iface.eContainer != null»
package «iface.eContainer.fullyQualifiedName»;

«FOR i:iface.eContainer.eContents.filter(typeof(Import))»
import «rootPackage».«i.importedNamespace»;
«ENDFOR»
«ENDIF»

/**
* Generated model interface «iface.name».
* @author TODO
* @version TODO
*/
public interface «iface.name» {
«FOR m:iface.methods»
«m.compileMethodInterface»
«ENDFOR»
}
'''

def compileMethodInterface(Method p) '''

/**
* TODO: Method description...
* «IF p.params != null»@param TODO: describle all parameters...«ENDIF»
* «IF !p.type.fullyQualifiedName.toString.equals("void")»@return TODO: Return value description...«ENDIF»
*/
public «p.type.fullyQualifiedName.lastSegment» «p.name.toFirstLower»(«FOR prm:p.params SEPARATOR ", "»«prm.type.fullyQualifiedName.lastSegment» «prm.name»«ENDFOR»);
'''
// ...


Das wars! Legt man nun in der zweiten Eclipse-Instanz noch ein /src-gen Ordner im Projekt an, wird zum definierten textuellen Model sofort der gewünschte Code generiert.

Comments

Popular posts from this blog

UWPCore: A development acceleration framework for the Universal Windows Platform

How to setup an eGPU on Ubuntu for TensorFlow

Benchmarking Tensorflow Performance on eGPU